在NestJS中用Mercurius实现GraphQL查询复杂度限制遇错求助
在NestJS + Mercurius中实现GraphQL查询复杂度限制的解决方案
问题原因
你直接使用了Apollo GraphQL的createComplexityRule插件,但该插件依赖Apollo特定的插件上下文结构,Mercurius的插件API提供的上下文对象与Apollo不兼容,导致调用this.context.getSchema()时出现undefined错误。
解决方案
放弃Apollo的插件,改用Mercurius原生插件API自定义复杂度计算逻辑,以下是具体实现步骤:
1. 自定义Mercurius复杂度限制插件
创建一个实现MercuriusPlugin接口的插件,在preValidation阶段解析查询AST并计算复杂度:
import { Plugin } from '@nestjs/graphql'; import { MercuriusPlugin } from '@nestjs/graphql/dist/drivers/mercurius.driver'; import { visit, DocumentNode, GraphQLError } from 'graphql'; @Plugin() export class ComplexityLimitPlugin implements MercuriusPlugin { async preValidation(schema: any, document: DocumentNode) { const MAX_COMPLEXITY = 2; let totalComplexity = 0; // 遍历查询AST计算总复杂度 visit(document, { Field(node) { // 处理include/skip指令:跳过被skip的字段,仅计算被include或无include指令的字段 const isSkipped = node.directives?.some(d => d.name.value === 'skip'); const isIncluded = !isSkipped && ( !node.directives?.some(d => d.name.value === 'include') || node.directives?.some(d => { // 处理include指令的if参数(如果需要) const ifArg = d.arguments?.find(a => a.name.value === 'if'); return ifArg?.value.kind === 'BooleanValue' ? ifArg.value.value : true; }) ); if (isIncluded) { // 读取schema中定义的字段复杂度,默认1 const parentType = schema.getType(node.parent.name.value); const fieldDef = parentType.getFields()[node.name.value]; const fieldComplexity = fieldDef.extensions?.complexity || 1; totalComplexity += fieldComplexity; } }, }); // 超过阈值则抛出错误 if (totalComplexity > MAX_COMPLEXITY) { throw new GraphQLError( `Query is too complex: ${totalComplexity}. Maximum allowed complexity: ${MAX_COMPLEXITY}`, ); } return document; } }
2. 在GraphQL模块中注册插件
修改你的GraphQL模块配置,注册自定义插件:
import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { MercuriusDriver } from '@nestjs/graphql/dist/drivers/mercurius.driver'; import { ComplexityLimitPlugin } from './complexity-limit.plugin'; @Module({ imports: [ GraphQLModule.forRoot({ driver: MercuriusDriver, autoSchemaFile: true, // 或你的schema路径 // 其他配置... plugins: [ComplexityLimitPlugin], }), ], }) export class AppModule {}
3. 可选:在Schema中定义字段自定义复杂度
如果需要给特定字段设置不同的复杂度,可以在schema中通过扩展字段添加:
type Query { # 设置该字段复杂度为2 users: [User!]! @complexity(value: 2) # 默认复杂度为1 user(id: ID!): User }
对应的,在插件中已经处理了读取extensions?.complexity的逻辑,会自动使用你定义的值。
关键说明
- Mercurius的插件生命周期与Apollo不同,
preValidation阶段是在查询验证前执行,适合做复杂度检查 - 手动遍历AST可以完全控制复杂度计算逻辑,适配Mercurius的上下文环境
- 无需依赖Apollo的插件,避免了上下文不兼容的问题
内容的提问来源于stack exchange,提问作者stupid dev
相关产品推荐
相关产品推荐

