NestJS GraphQL是否需自定义装饰器访问@Req/@Res对象
原生@Req()/@Res()不生效的原因
NestJS原生的HTTP参数装饰器默认仅从HTTP路由执行上下文中读取对象,而GraphQL解析器运行在独立的GraphQL执行上下文,不属于常规HTTP路由处理流程,因此直接使用原生装饰器无法正确注入请求、响应对象,测试不生效属于预期行为。
问题1:GraphQL场景下是否需要自定义装饰器获取Req/Res
自定义装饰器是官方推荐的标准实践。
当前v9及以上版本的@nestjs/graphql包已内置@GqlReq()装饰器可直接用于获取请求对象,但官方未提供对应的响应对象装饰器,获取Res对象仍需通过GqlExecutionContext自定义装饰器实现,自定义装饰器可同时兼容HTTP调用、WebSocket订阅等多GraphQL传输场景。
问题2:GraphQL中正确获取响应对象的方式
你自定义装饰器里的取值逻辑是正确的,代码智能提示无法识别属性是TypeScript类型缺失导致的,不是逻辑错误。
核心取值逻辑
GraphQL执行上下文默认会将原始HTTP请求对象挂载到上下文的req属性上,而Express/Fastify等底层HTTP框架的响应对象,本身就挂载在原始请求对象的res属性上,因此ctx.getContext().req.res的写法在标准配置下完全可用。
带类型支持的装饰器实现(解决智能提示问题)
先实现请求装饰器(如果使用高版本Nest可跳过,直接用内置@GqlReq()):
import { createParamDecorator, ExecutionContext } from "@nestjs/common"; import { GqlExecutionContext } from "@nestjs/graphql"; import { Request } from "express"; // 若使用Fastify驱动,替换为fastify对应的Request类型导入 export const GqlReq = createParamDecorator( (_data: unknown, context: ExecutionContext): Request => { if (context.getType() === "http") { return context.switchToHttp().getRequest<Request>(); } const ctx = GqlExecutionContext.create(context); return ctx.getContext().req; } );
再实现响应装饰器,通过类型断言解决TS提示问题:
import { createParamDecorator, ExecutionContext } from "@nestjs/common"; import { GqlExecutionContext } from "@nestjs/graphql"; import { Response } from "express"; // 若使用Fastify驱动,替换为fastify对应的Response类型导入 export const GqlRes = createParamDecorator( (_data: unknown, context: ExecutionContext): Response => { if (context.getType() === "http") { return context.switchToHttp().getResponse<Response>(); } const ctx = GqlExecutionContext.create(context); // 加类型断言即可解决IDE智能提示不识别的问题 return ctx.getContext().req.res as Response; } );
前置配置检查
如果取值报错,先检查GraphQL模块初始化时的context配置,必须手动透传req和res对象才能在上下文中访问到:
// app.module.ts中GraphQLModule配置示例 GraphQLModule.forRoot({ // 其余GraphQL配置(驱动、playground、schema路径等) context: ({ req, res }) => ({ req, res }), // 必须显式返回req、res })
解析器中使用示例
和HTTP场景用法一致,注入响应对象时需要加passthrough: true避免Nest判定为手动接管响应,导致解析器返回值无法正常序列化返回:
import { Query } from "@nestjs/graphql"; import { GqlReq, GqlRes } from "./common/decorators"; // 若使用高版本内置GqlReq,可直接从@nestjs/graphql导入 @Query(() => User) async getUser( @GqlReq() req: RequestWithUser, @GqlRes({ passthrough: true }) res: Response, ): Promise<User> { // 直接调用cookie方法设置即可 res.cookie("auth_token", "your_jwt_value", { httpOnly: true, maxAge: 7 * 24 * 3600 * 1000 }); return { email: "admin@gmail.com" }; }
内容的提问来源于stack exchange,提问作者wongx

