You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GraphQL @Mutation与自定义Log装饰器顺序异常及方法日志方案咨询

优先排查内容

  • 自定义装饰器是否丢失原方法元数据
    TS/JS的方法装饰器执行顺序为从下往上(离方法越近的装饰器越先执行),你原来的写法@Mutation()在上、@Log()在下,执行顺序为先执行@Log()替换方法实现,再执行@Mutation()给替换后的方法挂载GraphQL所需的返回类型、参数定义、Resolver标识等元数据,因此可以正常运行。调换顺序后会先执行@Mutation()给原方法挂载元数据,再执行@Log()将方法替换为新的匿名函数,新函数没有继承原方法的元数据,GraphQL读取不到所需配置就会报错。
  • 装饰器内返回值处理是否正确
    你当前写的@Log()装饰器里调用原方法时没有加await,打印的result实际是Promise对象而非真实返回值,虽然不是直接报错原因,但属于功能缺陷。
  • 装饰器返回的descriptor属性是否正确
    确认你没有修改descriptor的writable、enumerable等默认属性,避免影响其他装饰器的正常读取。

可行解决方案

方案1:修复自定义Log装饰器

补充元数据复制逻辑,保证原方法挂载的所有元数据都能继承到替换后的新方法上,修复后无论装饰器顺序如何都能正常运行:

import { copyMetadata } from '@nestjs/common/utils/copy-metadata.util';

export const Log = (): MethodDecorator => (target: Object, propertyKey: string, descriptor: PropertyDescriptor) => {
  const originalMethod = descriptor.value;

  const newMethod = async function (...args) {
    console.log('输入参数:', args);
    // 增加await获取真实返回结果
    const result = await originalMethod.apply(this, args);
    console.log('输出结果:', result);
    return result;
  };

  // 复制原方法的所有元数据到新方法
  copyMetadata(originalMethod, newMethod);
  descriptor.value = newMethod;

  return descriptor;
};

方案2:使用NestJS拦截器实现日志采集(更推荐)

无需给每个方法加装饰器,无侵入性,不会和现有GraphQL装饰器产生冲突:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler, UseInterceptors } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { GqlExecutionContext } from '@nestjs/graphql';

@Injectable()
export class GraphqlLoggerInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const gqlCtx = GqlExecutionContext.create(context);
    const resolverName = gqlCtx.getHandler().name;
    const inputArgs = gqlCtx.getArgs();
    
    console.log(`[Resolver: ${resolverName}] 输入参数:`, inputArgs);
    return next.handle().pipe(
      tap(res => console.log(`[Resolver: ${resolverName}] 输出结果:`, res))
    );
  }
}

使用时可以全局注册,或者给单个Resolver/方法加@UseInterceptors(GraphqlLoggerInterceptor)即可。

方案3:使用Apollo GraphQL插件

直接利用Apollo Server的生命周期钩子做全局日志采集,完全不侵入业务代码,适合需要统一采集所有GraphQL请求日志的场景。

内容的提问来源于stack exchange,提问作者gentlejo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 10:15:05