Docs
Launch GraphOS Studio

Context and contextValue

Sharing information and request details throughout your server


During a , 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 might need, like authentication scope, sources for fetching data, database connections, and custom fetch functions. If you're using dataloaders to batch requests across , 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):

import { GraphQLError } from 'graphql';
const resolvers = {
Query: {
// Example resolver
adminExample: (parent, args, contextValue, info) => {
if (contextValue.authScope !== ADMIN) {
throw new GraphQLError('not admin!', {
extensions: { code: 'UNAUTHENTICATED' },
});
}
},
},
};
interface MyContext {
// You can optionally create a TS interface to set up types
// for your contextValue
authScope?: String;
}
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
// Your async context function should async and
// return an object
context: async ({ req, res }) => ({
authScope: getScope(req.headers.authorization),
}),
});
import { GraphQLError } from 'graphql';
const resolvers = {
Query: {
// Example resolver
adminExample: (parent, args, contextValue, info) => {
if (contextValue.authScope !== ADMIN) {
throw new GraphQLError('not admin!', {
extensions: { code: 'UNAUTHENTICATED' },
});
}
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
// Your async context function should async and
// return an object
context: async ({ req, res }) => ({
authScope: getScope(req.headers.authorization),
}),
});

The above example assumes you're using either startStandaloneServer or expressMiddleware, both of which use Express under the hood. Your context function's incoming 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 to complete:

context: async () => ({
db: await client.connect(),
})
// Resolver
(parent, args, contextValue, info) => {
return contextValue.db.query('SELECT * FROM table_name');
}

Throwing errors

By default, if your context function throws an error, 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:

context: async ({ req }) => {
const user = await getUserFromReq(req);
if (!user) {
throw new GraphQLError('User is not authenticated', {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: 401 },
}
});
}
// If the below throws a non-GraphQLError, the server returns
// `code: "INTERNAL_SERVER_ERROR"` with an HTTP status code 500, and
// a message starting with "Context creation failed: ".
const db = await getDatabaseConnection();
return { user, db };
},

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 . All resolvers that are executing for a particular operation have access to contextValue:

import { AnimalAPI } from './datasources/animals';
const resolvers = {
Query: {
// All of our resolvers can access our shared contextValue!
dogs: (_, __, contextValue) => {
return contextValue.dataSources.animalApi.getDogs();
},
cats: (_, __, contextValue) => {
return contextValue.dataSources.animalApi.getCats();
},
},
};
interface MyContext {
// Context typing
dataSources: {
animalApi: AnimalAPI;
};
}
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
context: async () => {
const animalApi = new AnimalAPI();
return {
dataSources: {
animalApi,
},
};
},
});
import { AnimalAPI } from './datasources/animals';
const resolvers = {
Query: {
// All of our resolvers can access our shared contextValue!
dogs: (_, __, contextValue) => {
return contextValue.dataSources.animalApi.getDogs();
},
cats: (_, __, contextValue) => {
return contextValue.dataSources.animalApi.getCats();
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
context: async () => {
const animalApi = new AnimalAPI();
return {
dataSources: {
animalApi,
},
};
},
});

Plugins

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

interface MyContext {
token: string;
}
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers: {
Query: {
hello: (root, args, { token }) => {
return token;
},
},
},
plugins: [
{
async requestDidStart({ contextValue }) {
// token is properly inferred as a string
console.log(contextValue.token);
},
},
],
});
const { url } = await startStandaloneServer(server, {
context: async ({ req, res }) => ({
token: await getTokenForRequest(req),
}),
});
const server = new ApolloServer({
typeDefs,
resolvers: {
Query: {
hello: (root, args, { token }) => {
return token;
},
},
},
plugins: [
{
async requestDidStart({ contextValue }) {
// token is properly inferred as a string
console.log(contextValue.token);
},
},
],
});
const { url } = await startStandaloneServer(server, {
context: async ({ req, res }) => ({
token: await getTokenForRequest(req),
}),
});
Previous
Resolvers
Next
Error handling
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company