跳到主要内容

复杂度

备注

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

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

@nestjs/graphql 包与 graphql-query-complexity 这样的工具非常好地集成在一起,提供了一种基于成本分析的解决方案。 使用该库,您可以拒绝认为执行成本过高的 GraphQL 服务器的查询。

安装

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

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

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

记得将这个类添加到任何模块中的提供者数组中。

字段级别的复杂度

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