Context and contextValue

Sharing information and request details throughout your server


During a GraphQL operation, you can share data throughout your server's resolvers and plugins by creating an object named contextValue.

You can pass useful things through your contextValue that any resolver might need, like authentication scope, sources for fetching data, database connections, and custom fetch functions. If you're using dataloaders to batch requests across resolvers, you can also attach them to the shared contextValue.

The context function

📣 Apollo Server 4 changes the syntax for defining a context function. See more details.

The context function should be asynchronous and return an object. This object is then accessible to your server's resolvers and plugins using the name contextValue.

You can pass a context function to your integration function of choice (e.g., expressMiddleware or startStandaloneServer).

Your server calls the context function once for every request, enabling you to customize your contextValue with each request's details (such as HTTP headers):

TypeScript
1import { GraphQLError } from 'graphql';
2
3const resolvers = {
4  Query: {
5    // Example resolver
6    adminExample: (parent, args, contextValue, info) => {
7      if (contextValue.authScope !== ADMIN) {
8        throw new GraphQLError('not admin!', {
9          extensions: { code: 'UNAUTHENTICATED' },
10        });
11      }
12    },
13  },
14};
15
16interface MyContext {
17// You can optionally create a TS interface to set up types
18// for your contextValue
19  authScope?: String;
20}
21
22const server = new ApolloServer<MyContext>({
23  typeDefs,
24  resolvers,
25});
26
27const { url } = await startStandaloneServer(server, {
28  // Your async context function should async and
29  // return an object
30  context: async ({ req, res }) => ({
31    authScope: getScope(req.headers.authorization),
32  }),
33});

The above example assumes you're using either startStandaloneServer or expressMiddleware, both of which use Express under the hood. Your context function's incoming arguments might differ if you're using a different integration.

If you are using TypeScript, you must provide a named context function if you type your context by passing a type parameter to ApolloServer (i.e., you don't use ApolloServer<BaseContext>).

Because the context initialization function is asynchronous, you can use it to establish database connections and wait for other operations to complete:

TypeScript
1context: async () => ({
2  db: await client.connect(),
3})
4
5// Resolver
6(parent, args, contextValue, info) => {
7  return contextValue.db.query('SELECT * FROM table_name');
8}

Throwing errors

By default, if your context function throws an error, Apollo Server returns that error in a JSON response with a 500 HTTP status code. If the error is not a GraphQLError, the error's message is prepended with "Context creation failed: ".

You can change the HTTP status code of an error by throwing a GraphQLError with an http extension. For example:

TypeScript
1context: async ({ req }) => {
2  const user = await getUserFromReq(req);
3  if (!user) {
4    throw new GraphQLError('User is not authenticated', {
5      extensions: {
6        code: 'UNAUTHENTICATED',
7        http: { status: 401 },
8      }
9    });
10  }
11
12  // If the below throws a non-GraphQLError, the server returns
13  // `code: "INTERNAL_SERVER_ERROR"` with an HTTP status code 500, and
14  // a message starting with "Context creation failed: ".
15  const db = await getDatabaseConnection();
16
17  return { user, db };
18},

The contextValue object

The context function returns an object, contextValue, that is accessible to your plugins and resolvers.

Resolvers

Resolvers should never destructively modify the contextValue argument. This ensures consistency across all resolvers and prevents unexpected errors.

Your resolvers can access the shared contextValue object via their third positional argument. All resolvers that are executing for a particular operation have access to contextValue:

TypeScript
1import { AnimalAPI } from "./datasources/animals";
2
3const resolvers = {
4  Query: {
5    // All of our resolvers can access our shared contextValue!
6    dogs: (_, __, contextValue) => {
7      return contextValue.dataSources.animalApi.getDogs();
8    },
9    cats: (_, __, contextValue) => {
10      return contextValue.dataSources.animalApi.getCats();
11    },
12  },
13};
14
15interface MyContext { // Context typing
16  dataSources: {
17    animalApi: AnimalAPI;
18  }
19}
20
21const server = new ApolloServer<MyContext>({
22  typeDefs,
23  resolvers,
24});
25
26const { url } = await startStandaloneServer(server, {
27  context: async () => {
28    const animalApi = new AnimalAPI();
29    return {
30      dataSources: {
31        animalApi
32      }
33    }
34  }
35});

Plugins

Built-in and custom plugins can access contextValue through request lifecycle functions, like so:

TypeScript
1interface MyContext {
2  token: string
3}
4
5const server = new ApolloServer<MyContext>({
6  typeDefs,
7  resolvers: {
8    Query: {
9      hello: (root, args, { token }) => {
10        return token;
11      },
12    },
13  },
14  plugins: [{
15    async requestDidStart({ contextValue }) {
16      // token is properly inferred as a string
17      console.log(contextValue.token);
18    },
19  }],
20});
21
22const { url } = await startStandaloneServer(server, {
23  context: async ({req, res}) => ({
24    token: await getTokenForRequest(req),
25  })
26});
Feedback

Edit on GitHub

Forums