NestJS 9 GraphQL 中文文档(十二) - 复杂度

167 阅读2分钟

Nest 最新的版本为V9,较之前的V7/8有不小改动,GraphQL 部分官方文档之前的V8也是本人翻译的,有很多不完善的地方,这次打算重新精细翻译一遍,会持续更新这块内容,并最后贡献给中文文档仓库,为中文社区贡献一份力量。有兴趣的小伙伴记得要关注收藏。

警告 ⚠️
此章节仅适用于代码优先方式

查询复杂度允许你定义某些字段的复杂程度,并限制具有最大复杂度的查询。这个理念是通过使用一个简单的数字来定义你每个字段的复杂程度。通常每个字段的默认复杂度设置是1。此外,GraphQL 查询的复杂度计算可以使用所谓的复杂度估算器进行定制。复杂度估算器是一个简单的函数,用于计算字段的复杂度。你可以将任意数量的复杂度估算器添加到规则中,然后一个接一个地执行。返回数字复杂度值的第一个估算器确定该字段的复杂度。

@nestjs/graphql包与graphql-query-complexity等工具很好地集成,提供了基于成本分析的解决方案。

安装

首先,我们安装所需的依赖项。

$ npm install --save graphql-query-complexity

入门

一旦安装好之后,我们就可以定义这个ComplexityPlugin 类:

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。在上面的例子中,我们使用两个估算器,即simpleEstimatorfieldExtensionsEstimator

  • simpleEstimator:这个简单的估算器为每个字段返回一个固定的复杂度。
  • fieldExtensionsEstimator:字段扩展估算器提取schema的每个字段的复杂度值。

提示 别忘了将此类添加到任意模块中的提供者数组中。

字段级复杂度

有了这个插件,我们就可以通过在传入到@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 });
}