Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I correctly type the context object in Apollo Server?

I'm using apollo server with typescript and I'm having trouble getting the context parameter inside of my resolver to pick up that the name property on context is a string. Right now it's typed as any and I'd like it to be typed as string. I also see that the context parameter is of type any, when I'd like it to be a specific interface. Is there anyway to tell context and it's properties what type I want it to be rather than them all being typed as any?

const server = new ApolloServer({
  typeDefs: gql`
    type Query {
      test: String
    }
  `,
  resolvers: {
    Query: {
      test(parent: any, args: any, context, info: any) {
        context.name // name is typed as "any" when I'd like it to be typed as "string"
      }
    }
  },
  context() {
    return { 
      name: 'John Doe' 
    }
  }
})

I tried to do something like this, but that's throwing an error.

context<{ name: string }>() {
  return { 
    name: 'John Doe' 
  }
}

I did get it to work by explicitly telling context what it needs to be, but this way seems a bit tedious as I have to import Context into every resolver file and cast manually.

interface Context {
  name: string
}

const server = new ApolloServer({
  ...
  resolvers: {
    Query: {
      test(parent: any, args: any, context: Context, info: any) {
        context.name // name is typed as "string" here because I used "context: Context" explicitly
      }
    }
  }
  ...
})
like image 767
Dennis Martinez Avatar asked Jul 29 '20 17:07

Dennis Martinez


People also ask

How do I enable introspection on Apollo Server?

To enable introspection, set introspection: true in the options to ApolloServer 's constructor. You can also: Select Automatically redirect to Studio next time if you want to open Sandbox automatically whenever you visit localhost:4000. Open Sandbox directly at studio.apollographql.com/sandbox.

What is context in GraphQL?

In GraphQL, a context is an object shared by all the resolvers of a specific execution. It's useful for keeping data such as authentication info, the current user, database connection, data sources and other things you need for running your business logic.

What is a resolver Apollo?

A resolver is a function that's responsible for populating the data for a single field in your schema. It can populate that data in any way you define, such as by fetching data from a back-end database or a third-party API.


Video Answer


2 Answers

If you're using graphql-code-generator to generate your types you can configure the context type in codegen.yml:

generates:
  src/generated/graphql.ts:
    config:
      contextType: "src/types#Context"

Then define your context type:

export interface Context {
  username: string;
}

Now the context argument of your resolver methods will be typed Context without having to import types or make any other changes.

like image 110
palmsey Avatar answered Oct 18 '22 18:10

palmsey


By default the resolvers field on the Config object you're giving to ApolloServer is open to any types, but you can use TypeScript to do your own type narrowing.

For reference, ApolloServer accepts an instance of or array of IResolvers objects as declared here: https://github.com/apollographql/apollo-server/blob/a241d34e9275bf6a23cf7aa3ddee57f90de7b364/packages/apollo-server-core/src/types.ts#L90. IResolvers has generic type parameters, but those default to any: https://github.com/ardatan/graphql-tools/blob/8c8d4fc09ddc63c306db16d7386865ac297794bd/packages/utils/src/Interfaces.ts#L298. In your code sample, Query is one of the specific types that make up IResolvers and test() implements IFieldResolver: https://github.com/ardatan/graphql-tools/blob/8c8d4fc09ddc63c306db16d7386865ac297794bd/packages/utils/src/Interfaces.ts#L236.

You can take advantage of the fact that IResolvers has generic parameters to restrict the type of the context argument. For example, by explicitly specifying a type on the left side of an assignment, you can tell the compiler what to expect on the right:

interface Context {
  username: string
}

const myResolvers: IResolvers<any, Context> = {
  Query: {
    test: (parent, args, context) => {
      console.log(context.username) // based on generics TS knows context is supposed to be of type Context
    }
  }
}

new ApolloServer({
  ...
  resolvers: myResolvers,
  ...
like image 25
Tim Ernsberger Avatar answered Oct 18 '22 19:10

Tim Ernsberger