如何限制查询内省?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
相关产品推荐
相关产品推荐

