skip to Main Content

I have a simple Apollo GraphQL subgraph where I want to set the type for my context arg in my resolvers.

I create a resolver and set the type for context as follows:

interface Context {
  dataSources: {
    shopify: Shopify;
  }
}

const server = new ApolloServer({
  schema: buildSubgraphSchema({ 
    typeDefs,
    resolvers: {
      Query: {
        shop(_, _args, ctx: Context) {
          console.log("ctx:", ctx)
        },
      }
    }
  }),
  dataSources: () => ({
    shopify: new Shopify()
  })
});

However, it appears the Context interface I am setting is incompatible with the type my resolver expects and I get the following error:

Argument of type '{ typeDefs: any; resolvers: { Query: { shop(_: any, _args: any, ctx: Context): void; }; }; }' is not assignable to parameter of type 'DocumentNode | (GraphQLSchemaModule | DocumentNode)[] | LegacySchemaModule'.
  Types of property 'resolvers' are incompatible.
    Property 'Query' is incompatible with index signature.
      Type '{ shop(_: any, _args: any, ctx: Context): void; }' is not assignable to type 'GraphQLScalarType<unknown, unknown> | { [enumValue: string]: string | number; } | { [fieldName: string]: GraphQLFieldResolver<any, unknown, any, unknown> | { requires?: string | undefined; resolve: GraphQLFieldResolver<...>; }; }'.
        Types of property 'shop' are incompatible.
          Type '(_: any, _args: any, ctx: Context) => void' is not assignable to type 'string | number | GraphQLFieldResolver<any, unknown, any, unknown> | { requires?: string | undefined; resolve: GraphQLFieldResolver<any, unknown, any, unknown>; } | undefined'.
            Type '(_: any, _args: any, ctx: Context) => void' is not assignable to type 'GraphQLFieldResolver<any, unknown, any, unknown>'.
              Types of parameters 'ctx' and 'context' are incompatible.
                Type 'unknown' is not assignable to type 'Context'.ts(2345)

How can I set the context type for my resolver so I can get access to the interface and datasources etc?

2

Answers


  1. The ApolloServer class is generic, you should specify your custom context as a type parameter. See the docs and these unit tests.

    interface Context {
      dataSources: {
        shopify: Shopify;
      }
    }
    
    const server = new ApolloServer<Context>({
    //                             ^^^^^^^^^
      schema: buildSubgraphSchema({ 
        typeDefs,
        resolvers: {
          Query: {
            shop(_, _args, ctx: Context) {
              console.log("ctx:", ctx)
            },
          }
        }
      }),
      dataSources: () => ({
        shopify: new Shopify()
      })
    });
    
    Login or Signup to reply.
  2. OK I think I found a better solution.

    First things first lets really understand what buildSubgraphSchema can accept:

    buildSubgraphSchema(
      modulesOrSDL:
        | (GraphQLSchemaModule | DocumentNode)[] //interesting...
        | DocumentNode
        | LegacySchemaModule, //our tried and true way
    ): GraphQLSchema
    

    The problem is we are trying to pass in a LegacySchemaModule ({typeDefs, resolvers}) but due to the <unknown> generic being passed in to the GraphQLResovlerMap creates a conflict with an explicitly defined Context

    type LegacySchemaModule = {
      typeDefs: DocumentNode | DocumentNode[];
      resolvers?: GraphQLResolverMap<unknown>; //note `unknown`, the root of our typing errors
    };
    

    What we need to use in to actually act on context within the resolver is a GraphQLSchemaModule defined as follows:

    export interface GraphQLSchemaModule {
      typeDefs: DocumentNode;
      resolvers?: GraphQLResolverMap<any>; //note the `any` here
    }
    

    So with a resolver defined like this:

    export const resolvers: GraphQLResolverMap<Context> = {
      Query: {
        shop(_, _args, ctx) {
          console.log(ctx.datasources.shopify); // Shopify
        },
      },
    };
    

    We can now pass in an array of (GraphQLSchemaModule | DocumentNode)[]:

    const server = new ApolloServer({ 
        schema: buildSubgraphSchema([ //note the array
          {
            typeDefs, //DocumentNode
            resolvers, //GraphQLSchemaModule, in this case GraphQLResolverMap<Context>
          },
        ]),
      });
    

    That worked for me, no ts errors and the server started up great. Open to suggestions though from the community as to better patterns here.

    Ultimately I think codegen tools are designed to solve alot of these problems. I’m diving into this now: https://the-guild.dev/graphql/codegen

    Have fun!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search