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

如何限制查询内省?Apollo Server项目展示匿名用户视角Schema

实现匿名用户视角的GraphiQL Schema展示

我刚好处理过类似的场景,要让GraphiQL只展示匿名用户能访问的Schema元素(隐藏带@admin指令的查询、变更和字段),同时保留后端的权限校验逻辑,核心思路是生成一个过滤后的Schema专门给GraphiQL使用,而运行时依然用原始Schema做权限校验。

步骤1:编写Schema过滤函数

我们需要遍历原始Schema,移除所有带有@admin指令的查询、变更字段,以及对象类型中带@admin的字段。可以借助GraphQL的内置工具函数来实现:

const { getDirectives, GraphQLObjectType, GraphQLSchema } = require('graphql');

function filterAdminSchema(originalSchema) {
  // 过滤单个类型的字段(移除带@admin的字段)
  const filterFields = (type) => {
    const filteredFields = {};
    Object.entries(type.getFields()).forEach(([fieldName, field]) => {
      const directives = getDirectives(originalSchema, field.astNode);
      // 如果字段没有@admin指令,保留
      if (!directives.some(d => d.name.value === 'admin')) {
        filteredFields[fieldName] = field;
      }
    });
    return filteredFields;
  };

  // 处理Query类型
  const originalQuery = originalSchema.getQueryType();
  const filteredQuery = new GraphQLObjectType({
    name: originalQuery.name,
    description: originalQuery.description,
    fields: filterFields(originalQuery),
  });

  // 处理Mutation类型(如果有的话)
  let filteredMutation = null;
  const originalMutation = originalSchema.getMutationType();
  if (originalMutation) {
    filteredMutation = new GraphQLObjectType({
      name: originalMutation.name,
      description: originalMutation.description,
      fields: filterFields(originalMutation),
    });
  }

  // 处理所有对象类型(移除带@admin的字段)
  const filteredTypes = originalSchema.getTypeMap().reduce((acc, type, typeName) => {
    // 跳过内置类型(比如__Schema、__Type等)和已经处理过的Query/Mutation
    if (typeName.startsWith('__') || typeName === originalQuery.name || (originalMutation && typeName === originalMutation.name)) {
      acc[typeName] = type;
      return acc;
    }
    if (type instanceof GraphQLObjectType) {
      acc[typeName] = new GraphQLObjectType({
        name: type.name,
        description: type.description,
        interfaces: type.getInterfaces(),
        fields: filterFields(type),
      });
    } else {
      acc[typeName] = type;
    }
    return acc;
  }, {});

  // 创建新的Schema
  return new GraphQLSchema({
    query: filteredQuery,
    mutation: filteredMutation,
    types: Object.values(filteredTypes),
  });
}

步骤2:配置Apollo Server使用过滤后的Schema给GraphiQL

在Apollo Server的配置中,我们需要让运行时使用原始Schema(保证权限校验生效),但给GraphiQL传入过滤后的Schema:

const { ApolloServer } = require('apollo-server');
const originalSchema = require('./your-schema'); // 你的原始Schema

// 生成过滤后的Schema
const filteredSchema = filterAdminSchema(originalSchema);

const server = new ApolloServer({
  schema: originalSchema, // 运行时用原始Schema,保证@admin指令的权限校验正常工作
  graphiql: {
    schema: filteredSchema, // GraphiQL展示过滤后的Schema
    endpoint: '/graphql',
  },
  // 其他配置(context、resolvers等)
});

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);
});

关键细节说明

  • 运行时校验不受影响:后端依然使用原始Schema执行查询,@admin指令的权限逻辑(查询变更抛错、字段返回null)完全保留。
  • 嵌套字段处理:上述函数会遍历所有自定义对象类型,移除其中带@admin的字段,确保嵌套结构中敏感字段也不会在GraphiQL中显示。
  • 内置类型保留:过滤时跳过了GraphQL的内置元类型(比如__Schema),不会影响GraphiQL的正常功能。

测试一下,启动服务后打开GraphiQL界面,你会发现所有带@admin的查询、变更和字段都被隐藏了,完全是匿名用户的视角。

内容的提问来源于stack exchange,提问作者Konstantin Kuznetsov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:33:04