Apollo Server 4自定义指令迁移求助:原SchemaDirectiveVisitor方法失效
升级Apollo Server 4后自定义GraphQL指令的替代实现方案
背景说明
之前基于apollo-server-express的SchemaDirectiveVisitor构建自定义指令(如AuthDirective),并通过graphql-tools的SchemaDirectiveVisitor.visitSchemaDirectives方法将指令挂载到Schema:
class AuthDirective extends SchemaDirectiveVisitor { // 原指令逻辑实现 } const directives = { upper: UpperDirective, isAuth: AuthDirective }; SchemaDirectiveVisitor.visitSchemaDirectives(schema, directives);
但visitSchemaDirectives从graphql-tools v6起被移除,升级到Apollo Server 4后需改用基于Schema转换器的新实现方式。
新实现方案:Schema转换器函数
新版@graphql-tools/schema包中,需通过遍历Schema节点、手动应用指令逻辑的转换器函数替代原有挂载方式,具体步骤如下:
1. 重构自定义指令为节点处理函数
不再继承SchemaDirectiveVisitor,直接编写针对特定Schema节点的逻辑处理函数。以权限校验指令@isAuth为例:
import { defaultFieldResolver } from 'graphql'; // 定义@isAuth指令的核心逻辑 function applyAuthLogic(fieldConfig) { const { resolve = defaultFieldResolver } = fieldConfig; fieldConfig.resolve = async function (source, args, context, info) { // 原AuthDirective中的权限校验逻辑,比如检查上下文用户状态 if (!context.user) { throw new Error('未授权访问'); } return resolve(source, args, context, info); }; return fieldConfig; }
2. 创建Schema转换器
借助@graphql-tools/utils提供的工具,编写遍历Schema并应用指令逻辑的转换器:
import { mapSchema, getDirective, MapperKind } from '@graphql-tools/utils'; function authDirectiveTransformer(schema) { return mapSchema(schema, { // 处理所有标记了@isAuth指令的字段 [MapperKind.OBJECT_FIELD]: (fieldConfig) => { const authDirective = getDirective(schema, fieldConfig, 'isAuth')?.[0]; if (authDirective) { return applyAuthLogic(fieldConfig); } return fieldConfig; }, // 如需处理其他节点类型(如OBJECT_TYPE、INTERFACE),可添加对应MapperKind }); }
3. 在Apollo Server 4中应用转换器
先构建基础Schema,再通过转换器注入指令逻辑:
import { makeExecutableSchema } from '@graphql-tools/schema'; import { ApolloServer } from '@apollo/server'; import { expressMiddleware } from '@apollo/server/express4'; // 类型定义(包含指令声明) const typeDefs = ` directive @isAuth on FIELD_DEFINITION directive @upper on FIELD_DEFINITION type Query { protectedData: String @isAuth hello: String @upper } `; // 基础解析器 const resolvers = { Query: { protectedData: () => '敏感数据', hello: () => 'hello world', }, }; // 创建基础可执行Schema let schema = makeExecutableSchema({ typeDefs, resolvers }); // 应用指令转换器 schema = authDirectiveTransformer(schema); // 若有@upper指令,同理创建并应用对应的转换器 // schema = upperDirectiveTransformer(schema); // 初始化Apollo Server 4 const server = new ApolloServer({ schema }); // 后续Express中间件配置...
补充:@upper指令的转换器示例
function applyUpperLogic(fieldConfig) { const { resolve = defaultFieldResolver } = fieldConfig; fieldConfig.resolve = async function (source, args, context, info) { const result = await resolve(source, args, context, info); return typeof result === 'string' ? result.toUpperCase() : result; }; return fieldConfig; } function upperDirectiveTransformer(schema) { return mapSchema(schema, { [MapperKind.OBJECT_FIELD]: (fieldConfig) => { const upperDirective = getDirective(schema, fieldConfig, 'upper')?.[0]; if (upperDirective) { return applyUpperLogic(fieldConfig); } return fieldConfig; }, }); }
核心工具说明
mapSchema:遍历Schema所有节点,支持针对性修改getDirective:读取字段/类型上的指令配置MapperKind:定义Schema中可处理的节点类型(如字段、对象类型、接口等)
内容的提问来源于stack exchange,提问作者cipher_449449
相关产品推荐
相关产品推荐

