复杂度(Complexity)
警告
本章仅适用于代码优先方法。
查询复杂度允许你定义某些字段的复杂程度,并通过最大复杂度来限制查询。其思想是使用简单的数字来定义每个字段的复杂程度。常见的默认值是给每个字段分配复杂度 1。此外,GraphQL 查询的复杂度计算可以使用所谓的复杂度估算器进行自定义。复杂度估算器是一个简单的函数,用于计算字段的复杂度。你可以向规则中添加任意数量的复杂度估算器,它们会依次执行。第一个返回数值复杂度值的估算器将确定该字段的复杂度。
@nestjs/graphql 包与 graphql-query-complexity 等工具集成得非常好,该工具提供了基于成本分析的解决方案。使用此库,你可以拒绝那些被认为执行成本过高的 GraphQL 服务器查询。
安装
要开始使用,首先安装所需的依赖。
bash
$ npm install --save graphql-query-complexity快速开始
安装完成后,我们可以定义 ComplexityPlugin 类:
typescript
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:字段扩展估算器从 schema 中提取每个字段的复杂度值
提示
记得将此类添加到任何模块的 providers 数组中。
字段级别复杂度
有了此插件,我们现在可以通过在传递给 @Field() 装饰器的选项对象中指定 complexity 属性来定义任何字段的复杂度,如下所示:
typescript
@Field({ complexity: 3 })
title: string;或者,你可以定义估算器函数:
typescript
@Field({ complexity: (options: ComplexityEstimatorArgs) => ... })
title: string;查询/变更级别复杂度
此外,@Query() 和 @Mutation() 装饰器也可以指定 complexity 属性,如下所示:
typescript
@Query({ complexity: (options: ComplexityEstimatorArgs) => options.args.count * options.childComplexity })
items(@Args('count') count: number) {
return this.itemsService.getItems({ count });
}