查询复杂度 Complexity

查询复杂度允许您定义某些字段的复杂度,并使用最大复杂度限制查询。这个想法是使用一个简单的数字来定义每个字段的复杂度。常见的默认值是给每个字段的复杂度为`1`。此外,可以使用所谓的复杂度估算器自定义 GraphQL 查询的复杂度计算。复杂度估算器是一个简单的函数,用于计算字段的复杂度。您可以将任意数量的复杂度估算器添加到规则中,然后一个接一个地执行它们。返回数字复杂度值的第一个估算器确定该字段的复杂度。

警告

本章仅适用于代码优先方法。

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

安装

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

bash
$ npm install --save graphql-query-complexity

入门

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

ts
import { GraphQLSchemaHost } from '@nestjs/graphql'
import { Plugin } from '@nestjs/apollo'
import {
  ApolloServerPlugin,
  GraphQLRequestListener,
} from 'apollo-server-plugin-base'
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> {
    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 个估算器,即simpleEstimatorfieldExtensionsEstimator

-simpleEstimator:简单估算器为每个字段返回固定的复杂度 -fieldExtensionsEstimator:字段扩展估算器提取架构中每个字段的复杂度值

提示

请记住在任何模块中将此类添加到提供程序数组中。

字段级复杂度

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

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

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

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

Query/Mutation-level complexity

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

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