Can't resolve entities after ApolloServer4 upgrade

I have a federated graph that was working well, resolving entities from external services properly, the gateway doing his job.
After migrating one of my services who provide a subgraph to use ApolloServer4, the reference resolvers are no longer called on that service.

I have also tried querying for an entity directly on that service’s graphql endpoint (as opposed to through the gateway) and it’s not able to return anything except the @key and __typename

query ($representations: [_Any!]!) {
  _entities(representations: $representations) {
    ... on User {
      id
      email
      __typename
    }
  }
}

variables = {
  "representations": [
    {
      "__typename": "User",
      "id": 75
    }
  ]
}

returns error:

{
      "message": "Cannot return null for non-nullable field User.email.",
      "path": [
        "_entities",
        0,
        "email"
      ]...
}

The type and resolver looks like:


const typeDefs = gql`
  type User @key(fields: "id") {
    id: ID!
    email: String!
  }
`;
const UserResolver = {
    id: user => user.id,
    email: user => user.email,
    __resolveReference: async reference => {
    return UserModel.findOne({
      where: { id: reference.id }
    })}
}

const userSchema = {typeDefs, resolvers: {User: UserResolver}}

We are then using buildSubgraphSchema from @apollo/subgraph and schemaComposer from graphql-compose to generate the schema like so

const monolithSchema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: "Root",
    fields: () => ({})
  })
});

const typeDefsStr = printSchema(monolithSchema);

const monolithTypeDefs = gql(typeDefsStr);
const monolithResolvers = createResolversMap(monolithSchema);

let schema = buildSubgraphSchema([
  {
    typeDefs: monolithTypeDefs,
    resolvers: monolithResolvers
  },
  ...userSchema,
]);

schemaComposer.merge(schema);

schemaComposer
  .getOTC("User")
  .setDirectives([{ name: "key", args: { fields: "id" } }]);

schemaComposer.setDescription("Description");
const schema = schemaComposer.buildSchema();

This is all mostly unchanged after the ApolloServer4 upgrade.

Our ApolloServer definition looks like

const apolloServer = new ApolloServer<RequestContextType>({
    schema,
    introspection: true,
    includeStacktraceInErrorResponses: true,
    formatError,
    allowBatchedHttpRequests: true,
    plugins: [
      ObservabilityPlugin,
      ApolloServerPluginLandingPageGraphQLPlayground()
    ],
    stopOnTerminationSignals: false
  })

again, mostly unchanged.

Then there was the more substantial changes for ApolloServer4 that we made using the Migrate From apollo-server-express doc

const app = express();
await apolloServer.start();
const apolloMiddleware = expressMiddleware(apolloServer, {
    context: async ({ req: { tokenData, correlationId, headers } }) => {
      return {
        correlationId,
        headers
      };
    }
  });
app.use(
    "/graphql",
    buildTokenDataFromAuth0Token,
    setSchemaHash,
    cors<cors.CorsRequest>(),
    json({ limit: "5mb" }),
    apolloMiddleware
  );

Thanks for helping, please let me know if I can provide more information to help you help me!

Did you paste this code directly? Your apolloMiddleware is missing the call to expressMiddleware completely (but I’m guessing that’s a copy/paste error since the server is semi-responsive). Do regular operations work if you have any?

Did you upgrade any other dependencies at the same time?

There’s really nothing special or different about how AS4 handles the schema or “does GraphQL” vs AS3. I doubt the issue you’re seeing is attributed to the v4 upgrade, it seems like it’s something else. Right now schemaComposer is my top suspicion. Inspect the schema at different stages of being built. Is your __resolveReference intact and preserved on your User type after calling buildSubgraphSchema?

1 Like

Thanks, I accidentally deleted that when I was removing some typescript types for readability online. Updated the snippet above.

Other queries are working correctly, for example querying the User through the gateway instead of using the service’s endpoint we are able to resolve all fields of the User without error (doesn’t need to use entities/__resolveReference)

I did upgrade more deps at the same time. I’ll continue some research with your questions in mind

1 Like