NestJS Logo
NestJS 中文文档
v10.0.0
  • 介绍
  • 快速上手
  • 控制器
  • 提供者
  • 模块
  • 中间件
  • 异常过滤器
  • 管道
  • 守卫
  • 拦截器
  • 自定义装饰器
  • 自定义提供者
  • 异步提供者
  • 动态模块
  • 依赖注入作用域
  • 循环依赖
  • 模块引用
  • 懒加载模块
  • 执行上下文
  • 生命周期事件
  • 发现服务
  • 跨平台无关性
  • 测试
迁移指南
API 参考
官方课程
  1. 文档
  2. GraphQL 支持
  3. 复杂度

插件
扩展

复杂度(Complexity)

注意

本章节仅适用于代码优先(Code first)方式。

查询复杂度(Query complexity)允许你定义某些字段的复杂程度,并通过最大复杂度来限制查询。其核心思想是使用简单的数字来定义每个字段的复杂度。通常默认给每个字段分配复杂度为 1。此外,GraphQL 查询的复杂度计算可以通过所谓的复杂度估算器(complexity estimators)进行自定义。复杂度估算器是一个简单的函数,用于计算字段的复杂度。你可以向规则中添加任意数量的复杂度估算器,它们会依次执行。第一个返回数值复杂度的估算器将决定该字段的复杂度。

@nestjs/graphql 包与 graphql-query-complexity 等工具集成得非常好,该工具提供了基于成本分析的解决方案。通过这个库,你可以拒绝那些被认为执行成本过高的 GraphQL 服务器查询。

安装

要开始使用它,我们首先需要安装所需的依赖项。

$ npm install graphql-query-complexity

快速开始

安装完成后,我们可以定义 ComplexityPlugin 类:

import { GraphQLSchemaHost } from '@nestjs/graphql'
import { Plugin } from '@nestjs/apollo'
import {
  ApolloServerPlugin,
  BaseContext,
  GraphQLRequestListener,
} from '@apollo/server'
import { GraphQLError } from 'graphql'
import {
  fieldExtensionsEstimator,
  getComplexity,
  simpleEstimator,
} from 'graphql-query-complexity'

@Plugin()
export class ComplexityPlugin implements ApolloServerPlugin {
  constructor(private gqlSchemaHost: GraphQLSchemaHost) {}

  async requestDidStart(): Promise<GraphQLRequestListener<BaseContext>> {
    const maxComplexity = 20
    const { schema } = this.gqlSchemaHost

    return {
      async didResolveOperation({ request, document }) {
        const complexity = getComplexity({
          schema,
          operationName: request.operationName,
          query: document,
          variables: request.variables,
          estimators: [
            fieldExtensionsEstimator(),
            simpleEstimator({ defaultComplexity: 1 }),
          ],
        })
        if (complexity > maxComplexity) {
          throw new GraphQLError(
            `Query is too complex: ${complexity}. Maximum allowed complexity: ${maxComplexity}`
          )
        }
        console.log('Query Complexity:', complexity)
      },
    }
  }
}

为了演示目的,我们将最大允许复杂度指定为 20。在上面的示例中,我们使用了 2 个估算器:simpleEstimator 和 fieldExtensionsEstimator。

  • simpleEstimator:简单估算器为每个字段返回固定的复杂度
  • fieldExtensionsEstimator:字段扩展估算器从模式的每个字段中提取复杂度值
提示
记得将此类添加到任何模块的 providers 数组中。

字段级复杂度

有了这个插件,我们现在可以通过在传递给 @Field() 装饰器的选项对象中指定 complexity 属性来定义任何字段的复杂度,如下所示:

@Field({ complexity: 3 })
title: string

或者,你可以定义估算器函数:

@Field({ complexity: (options: ComplexityEstimatorArgs) => ... })
title: string

查询/变更级复杂度

此外,@Query() 和 @Mutation() 装饰器也可以指定 complexity 属性,如下所示:

@Query({ complexity: (options: ComplexityEstimatorArgs) => options.args.count * options.childComplexity })
items(@Args('count') count: number) {
  return this.itemsService.getItems({ count })
}