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

NestJS+GraphQL错误格式化:国际化与结构化处理问询

NestJS GraphQL 国际化错误处理与BAD_USER_INPUT格式优化

问题描述

我正在使用NestJS搭建GraphQL服务器,希望API的所有错误信息都能根据浏览器发送的lang请求头实现国际化。目前在处理非显式抛出的异常时遇到问题,尤其是GraphQL验证导致的BAD_USER_INPUT错误。我想实现类似nestjs-i18n结合class-validator进行全局验证的方案,考虑使用异常过滤器捕获BAD_USER_INPUT错误并通过nestjs-i18n格式化响应。但收到的错误格式如下:

{
    "message": "Variable \"$input\" got invalid value \"hello\" at \"input.id\"; Value is not a valid UUID: hello",
    "locations": [
        {
            "line": 1,
            "column": 37
        }
    ],
    "extensions": {
        "code": "BAD_USER_INPUT"
    }
}

该消息包含了错误位置、值及违反的约束,但格式不够直观。请问NestJS是否有内置方式将这些信息转换为更便捷的格式(比如包含字段路径的path属性)?还是只能通过解析消息提取信息,这是否是最简洁的实现方式?

解决方案

1. 内置能力说明

NestJS没有直接将此类错误消息转换为结构化path属性的内置方法,但可以通过自定义异常过滤器结合GraphQL的GraphQLError类重构错误结构。

2. 解析错误消息(最简洁实现)

解析原始错误消息提取结构化信息是当前最直接的方案,步骤如下:

  • 全局异常过滤器捕获BAD_USER_INPUT类型的GraphQL错误
  • 从消息中提取字段路径、错误值、约束类型
  • 结合nestjs-i18n根据lang请求头返回国际化消息
  • 重构错误响应,添加path等便捷属性到extensions

示例代码:

import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
import { GqlArgumentsHost } from '@nestjs/graphql';
import { GraphQLError } from 'graphql';
import { I18nService } from 'nestjs-i18n';

@Catch(GraphQLError)
export class GraphqlExceptionFilter implements ExceptionFilter {
  constructor(private readonly i18n: I18nService) {}

  async catch(error: GraphQLError, host: ArgumentsHost) {
    const gqlHost = GqlArgumentsHost.create(host);
    const lang = gqlHost.getContext().req.headers['lang'] || 'en';

    if (error.extensions.code === 'BAD_USER_INPUT') {
      const msgRegex = /Variable ".+" got invalid value "(.+)" at "(.+)"; (.+)/;
      const match = error.message.match(msgRegex);
      if (match) {
        const [, invalidValue, fieldPath, constraint] = match;
        // 提取约束关键词(如"Value is not a valid UUID")
        const constraintKey = constraint.split(':')[0].trim().replace(/\s+/g, '_').toLowerCase();
        
        // 国际化翻译
        error.message = await this.i18n.translate(`errors.${constraintKey}`, {
          lang,
          args: { field: fieldPath, value: invalidValue }
        });
        // 添加结构化字段
        error.extensions.path = fieldPath;
        error.extensions.invalidValue = invalidValue;
      }
    }
    return error;
  }
}

3. 替换默认验证器(进阶方案)

如果不想解析消息,可以替换GraphQL默认验证逻辑,用class-validator直接验证输入参数,错误会自带结构化字段路径:

  • 在GraphQL模块配置中启用validationPipe
  • 为输入DTO添加class-validator装饰器
  • 自定义异常工厂,结合nestjs-i18n生成国际化错误

示例配置:

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ValidationPipe } from '@nestjs/common';
import { I18nService } from 'nestjs-i18n';

@Module({
  imports: [
    GraphQLModule.forRootAsync({
      useFactory: (i18n: I18nService) => ({
        autoSchemaFile: 'schema.gql',
        context: ({ req }) => ({ req }),
        validationPipe: new ValidationPipe({
          transform: true,
          exceptionFactory: async (errors) => {
            const req = errors[0].context?.req;
            const lang = req?.headers['lang'] || 'en';
            // 转换验证错误为国际化格式
            const translatedErrors = await Promise.all(errors.map(async (err) => {
              const constraintKey = Object.keys(err.constraints)[0];
              return {
                field: err.property,
                message: await i18n.translate(`errors.${constraintKey}`, {
                  lang,
                  args: { value: err.value }
                })
              };
            }));
            return new Error(JSON.stringify(translatedErrors));
          }
        })
      }),
      inject: [I18nService]
    })
  ]
})
export class AppModule {}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 13:43:13