Skip to main content

其他特性

在GraphQL领域,有很多关于处理认证问题或操作副作用的讨论。 我们应该在业务逻辑内处理这些问题吗? 应该使用高阶函数通过授权逻辑增强查询和突变吗? 还是应该使用模式指令? 对于这些问题,没有一种适用于所有情况的单一解决方案。

Nest通过其跨平台功能(如守卫拦截器)来解决这些问题。 其哲学是减少冗余并提供工具,以帮助创建结构良好、可读且一致的应用程序。

概述

您可以像在任何RESTful应用程序中一样,使用标准的 守卫拦截器过滤器管道。 此外,您可以通过利用自定义装饰器功能轻松创建 自己的装饰器。 让我们看一个样本GraphQL查询处理程序。

@Query('author')
@UseGuards(AuthGuard)
async getAuthor(@Args('id', ParseIntPipe) id: number) {
return this.authorsService.findOneById(id);
}

正如您所看到的,GraphQL与守卫和管道的工作方式与HTTP REST处理程序相同。 因此,您可以将身份验证逻辑移至守卫;甚至可以在REST和GraphQL API界面之间重复使用相同的守卫类。 同样,拦截器在两种类型的应用程序中以相同的方式工作。

@Mutation()
@UseInterceptors(EventsInterceptor)
async upvotePost(@Args('postId') postId: number) {
return this.postsService.upvoteById({ id: postId });
}

执行上下文

由于GraphQL在传入请求中接收了一种不同类型的数据, 因此守卫和拦截器接收的执行上下文在GraphQL与REST之间略有不同。 GraphQL解析器具有一组不同的参数:root、args、context和info。 因此,守卫和拦截器必须将通用的ExecutionContext转换为GqlExecutionContext。 这很简单:

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const ctx = GqlExecutionContext.create(context);
return true;
}
}

GqlExecutionContext.create()返回的GraphQL上下文对象为每个GraphQL解析器参数 公开了一个get方法(例如,getArgs()getContext()等)。 转换后,我们可以轻松地提取当前请求的任何GraphQL参数。

异常过滤器

Nest标准的异常过滤器 在GraphQL应用程序中同样适用。与ExecutionContext一样, GraphQL应用程序应将ArgumentsHost对象转换为GqlArgumentsHost对象。

@Catch(HttpException)
export class HttpExceptionFilter implements GqlExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const gqlHost = GqlArgumentsHost.create(host);
return exception;
}
}
tip

GqlExceptionFilterGqlArgumentsHost都从@nestjs/graphql包中导入。

请注意,与REST情况不同,您不使用本机response对象生成响应。

自定义装饰器

如前所述,自定义装饰器 功能在GraphQL解析器中正常工作。

export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) =>
GqlExecutionContext.create(ctx).getContext().user,
);

使用@User()自定义装饰器如下:

@Mutation()
async upvotePost(
@User() user: UserEntity,
@Args('postId') postId: number,
) {}
note

在上面的示例中,我们假设user对象分配给了您的GraphQL应用程序的上下文。

在字段解析器级别执行增强器

在GraphQL上下文中,Nest不会在字段级别运行增强器(拦截器、守卫和过滤器的通用名称) 请参见此问题: 它们仅对顶级@Query()/@Mutation()方法运行。 您可以告诉Nest为带有@ResolveField()注解的方法执行拦截器、守卫或过滤器, 方法是在GqlModuleOptions中设置fieldResolverEnhancers选项。 将其传递给'interceptors''guards'和/或'filters'的列表:

GraphQLModule.forRoot({
fieldResolverEnhancers: ['interceptors']
}),
warning

启用字段解析器的增强器可能在返回大量记录并且字段解析器被执行数千次时导致性能问题。 因此,当启用fieldResolverEnhancers时, 我们建议您跳过对字段解析器不是严格必要的增强器的执行。 您可以使用以下辅助函数来实现:

export function isResolvingGraphQLField(context: ExecutionContext): boolean {
if (context.getType<GqlContextType>() === 'graphql') {
const gqlContext = GqlExecutionContext.create(context);
const info = gqlContext.getInfo();
const parentType = info.parentType.name;
return parentType !== 'Query' && parentType !== 'Mutation';
}
return false;
}

创建自定义驱动程序

Nest提供了两个内置的驱动程序:@nestjs/apollo@nestjs/mercurius, 以及一个API,允许开发人员构建新的自定义驱动程序。使用自定义驱动程序, 您可以集成任何GraphQL库或扩展现有的集成,添加额外的功能。

例如,要集成express-graphql包,您可以创建以下驱动程序类:

import { AbstractGraphQLDriver, GqlModuleOptions } from '@nestjs/graphql';
import { graphqlHTTP } from 'express-graphql';

class ExpressGraphQLDriver extends AbstractGraphQLDriver {
async start(options: GqlModuleOptions<any>): Promise<void> {
options = await this.graphQlFactory.mergeWithSchema(options);

const { httpAdapter } = this

.httpAdapterHost;
httpAdapter.use(
'/graphql',
graphqlHTTP({
schema: options.schema,
graphiql: true,
}),
);
}

async stop() {}
}

然后像这样使用它:

GraphQLModule.forRoot({
driver: ExpressGraphQLDriver,
});