Skip to content

字段中间件

警告

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

字段中间件允许你在字段解析之前或之后运行任意代码。字段中间件可用于转换字段的结果、验证字段的参数,甚至检查字段级别的角色(例如,检查访问目标字段所需的权限,该中间件函数将针对该字段执行)。

你可以将多个中间件函数连接到一个字段。在这种情况下,它们将沿着链顺序调用,前一个中间件决定是否调用下一个。middleware 数组中中间件函数的顺序很重要。第一个解析器是"最外层",因此它最先执行,也最后执行(类似于 graphql-middleware 包)。第二个解析器是"第二外层",因此它第二个执行,也倒数第二个执行。

入门

让我们从创建一个简单的中间件开始,它将在字段值发送回客户端之前记录该值:

typescript
import { FieldMiddleware, MiddlewareContext, NextFn } from '@nestjs/graphql';

const loggerMiddleware: FieldMiddleware = async (
  ctx: MiddlewareContext,
  next: NextFn,
) => {
  const value = await next();
  console.log(value);
  return value;
};

提示

MiddlewareContext 是一个对象,包含与 GraphQL 解析器函数通常接收的相同参数({ source, args, context, info }),而 NextFn 是一个函数,允许你执行堆栈中的下一个中间件(绑定到此字段)或实际的字段解析器。

警告

字段中间件函数不能注入依赖项,也不能访问 Nest 的 DI 容器,因为它们被设计为非常轻量级的,不应执行任何可能耗时的操作(如从数据库检索数据)。如果你需要调用外部服务/从数据源查询数据,应该在绑定到根查询/变更处理器的守卫/拦截器中完成,并将其分配给 context 对象,然后你可以从字段中间件中访问它(具体来说,从 MiddlewareContext 对象中访问)。

请注意,字段中间件必须匹配 FieldMiddleware 接口。在上面的示例中,我们首先运行 next() 函数(它执行实际的字段解析器并返回字段值),然后将此值记录到终端。此外,从中间件函数返回的值会完全覆盖之前的值,由于我们不想执行任何更改,因此我们只是返回原始值。

完成上述操作后,我们可以直接在 @Field() 装饰器中注册中间件,如下所示:

typescript
@ObjectType()
export class Recipe {
  @Field({ middleware: [loggerMiddleware] })
  title: string;
}

现在,每当我们请求 Recipe 对象类型的 title 字段时,原始字段的值将被记录到控制台。

提示

要了解如何使用扩展功能实现字段级权限系统,请查看此章节

警告

字段中间件只能应用于 ObjectType 类。有关更多详细信息,请查看此 issue

此外,如上所述,我们可以在中间件函数中控制字段的值。为了演示目的,让我们将食谱的标题转为大写(如果存在的话):

typescript
const value = await next();
return value?.toUpperCase();

在这种情况下,每个标题在被请求时将自动转为大写。

同样,你可以将字段中间件绑定到自定义字段解析器(使用 @ResolveField() 装饰器注解的方法),如下所示:

typescript
@ResolveField(() => String, { middleware: [loggerMiddleware] })
title() {
  return 'Placeholder';
}

警告

如果在字段解析器级别启用了增强器(了解更多),字段中间件函数将在任何拦截器、守卫等绑定到该方法的增强器之前运行(但在为查询或变更处理器注册的根级增强器之后运行)。

全局字段中间件

除了将中间件直接绑定到特定字段外,你还可以全局注册一个或多个中间件函数。在这种情况下,它们将自动连接到对象类型的所有字段。

typescript
GraphQLModule.forRoot({
  autoSchemaFile: 'schema.gql',
  buildSchemaOptions: {
    fieldMiddleware: [loggerMiddleware],
  },
}),

提示

全局注册的字段中间件函数将在本地注册的中间件函数(直接绑定到特定字段的中间件)之前执行。

基于 NestJS 官方文档翻译