上下文和contextValue
在服务器上共享信息和请求详情
在进行GraphQL 操作时,您可以在服务器上共享解析器和插件,通过创建一个名为contextValue
的
对象。contextValue
可以通过contextValue
传递任何解析器可能需要的有用信息,例如认证作用域、数据获取源、数据库连接和自定义获取函数。如果您正在使用数据加载器来批量处理解析器的请求,您还可以将它们附加到共享的contextValue
上。
context
函数
📣 Apollo Server 4 更改了定义context
函数的语法。查看更多详情。
context
函数应该是异步的并返回一个对象。这个对象随后可以通过使用名称contextValue
在您的服务器解析器和插件中访问。
您可以将一个context
函数传递到您的任何集成函数中(例如expressMiddleware
或startStandaloneServer
)。
服务器为每个请求调用一次context
函数,这使得您可以针对每个请求的详细信息(例如HTTP头)自定义您的contextValue
:
import { GraphQLError } from 'graphql';const resolvers = {Query: {// Example resolveradminExample: (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 contextValueauthScope?: String;}const server = new ApolloServer<MyContext>({typeDefs,resolvers,});const { url } = await startStandaloneServer(server, {// Your async context function should async and// return an objectcontext: async ({ req, res }) => ({authScope: getScope(req.headers.authorization),}),});
import { GraphQLError } from 'graphql';const resolvers = {Query: {// Example resolveradminExample: (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 objectcontext: async ({ req, res }) => ({authScope: getScope(req.headers.authorization),}),});
以上示例假设您正在使用 startStandaloneServer
或 expressMiddleware
,这两者都使用底层的 Express。您 context
函数的传入 参数可能会有所不同,如果您使用的是不同的集成。
如果您使用 TypeScript,您 必须 提供一个命名的 context
函数,如果您通过将类型参数传递给 ApolloServer
来对上下文进行类型化(即您不使用 ApolloServer<BaseContext>
)。
由于 context
初始化函数是异步的,您可以使用它来建立数据库连接并等待其他 操作 完成:
context: async () => ({db: await client.connect(),})// Resolver(parent, args, contextValue, info) => {return contextValue.db.query('SELECT * FROM table_name');}
抛出错误
默认情况下,如果您的 context
函数抛出错误,Apollo 服务器 将以 500 HTTP 状态码在 JSON 响应中返回该错误。如果错误不是 GraphQLError
,错误的消息将用 "上下文创建失败:"
预先添加。
您可以通过抛出一个具有 GraphQLError
和一个 http
扩展 来更改错误的 HTTP 状态码。例如:
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 };},
contextValue 对象
context
函数 返回一个对象,contextValue
,它对您的插件和解析器都是可访问的。
解析器
解析器不应该破坏性地修改 contextValue
参数。 这确保了所有 resolvers 之间的一致性,并且可以防止出现意外的错误。
您的解析器可以通过它们第三个位置参数访问共享的contextValue
对象。所有正在执行特定操作的解析器都有访问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 typingdataSources: {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,},};},});
插件
内置和自定义插件可以通过请求生命周期函数访问contextValue
,如下所示:
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 stringconsole.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 stringconsole.log(contextValue.token);},},],});const { url } = await startStandaloneServer(server, {context: async ({ req, res }) => ({token: await getTokenForRequest(req),}),});