You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 22:36:25