本章仅适用于代码优先方法。
字段中间件允许您在字段解析之前或之后运行任意代码。字段中间件可用于转换字段的结果、验证字段的参数,甚至检查字段级角色(例如,访问执行中间件函数的目标字段所需的角色)。
您可以将多个中间件函数连接到一个字段。在这种情况下,它们将沿着链按顺序调用,其中前一个中间件决定调用下一个中间件。middleware
数组中中间件函数的顺序很重要。第一个解析器是最外层
,因此它会首先执行,也是最后执行(类似于 graphql-middleware
包)。第二个解析器是第二外层
,因此它会从第二个到倒数第二个执行。
入门
让我们首先创建一个简单的中间件,它将在字段值发送回客户端之前记录该字段值:
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()
装饰器中注册我们的中间件,如下所示:
@ObjectType()
export class Recipe {
@Field({ middleware: [loggerMiddleware] })
title: string
}
现在,每当我们请求 Recipe
对象类型的 title
字段时,原始字段的值都会记录到控制台。
要了解如何使用 extensions 功能实现字段级权限系统,请查看此 section。
字段中间件只能应用于 ObjectType
类。有关更多详细信息,请查看此 issue。
此外,如上所述,我们可以从中间件函数中控制字段的值。为了演示目的,我们将菜谱的标题大写(如果存在):
const value = await next()
return value?.toUpperCase()
在这种情况下,每个标题在请求时都会自动大写。
同样,您可以将字段中间件绑定到自定义字段解析器(使用 @ResolveField()
装饰器注释的方法),如下所示:
@ResolveField(() => String, { middleware: [loggerMiddleware] })
title() {
return 'Placeholder';
}
如果在字段解析器级别启用了增强器(阅读更多),字段中间件函数将在任何拦截器、保护等绑定到方法之前运行(但在为查询或突变处理程序注册的根级增强器之后)。
全局字段中间件
除了将中间件直接绑定到特定字段外,您还可以全局注册一个或多个中间件函数。在这种情况下,它们将自动连接到对象类型的所有字段。
GraphQLModule.forRoot({
autoSchemaFile: 'schema.gql',
buildSchemaOptions: {
fieldMiddleware: [loggerMiddleware],
},
})
全局注册的字段中间件函数将在本地注册的函数(直接绑定到特定字段的函数)之前执行。