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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 11:57:05