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

如何在NestJS中全局修改TypeORM默认错误信息?

问题描述

我写了一段代码,用于在TypeORM中处理唯一列值重复的情况(错误码23505),自定义默认错误信息:

if (error.code === '23505') {
  // 最终消息格式:This COLUMN VALUE already exists.
  const message = error.detail.replace(
    /^Key \((.*)\)=\((.*)\) (.*)/,
    'The $1 $2 already exists.',
  );
  throw new BadRequestException(message);
}
throw new InternalServerErrorException();

这段代码需要在多个服务中复用,我原本考虑创建工具类,在需要的地方导入调用,但想知道有没有更好的方案——比如用全局过滤器或拦截器统一处理,不用在每个服务里都导入调用。请问这种全局处理的方式可行吗?如果可行该怎么实现?如果不可行,最优方案是什么?

以下是完整的服务代码示例:

@Injectable()
export class MerchantsService {
  constructor(
    @InjectRepository(Merchant)
    private merchantRepository: Repository<Merchant>,
  ) {}

  public async create(createMerchantDto: CreateMerchantDto) {
    try {
      const user = this.merchantRepository.create({
        ...createMerchantDto,
        documentType: DocumentType.NIT,
        isActive: false,
      });
      await this.merchantRepository.save(user);

      const { password, ...merchantData } = createMerchantDto;

      return {
        ...merchantData,
      };
    } catch (error) {
      if (error.code === '23505') {
        // 最终消息格式:This COLUMN VALUE already exists.
        const message = error.detail.replace(
          /^Key \((.*)\)=\((.*)\) (.*)/,
          'The $1 $2 already exists.',
        );
        throw new BadRequestException(message);
      }
      throw new InternalServerErrorException();
    }
  }

  public async findOneByEmail(email: string): Promise<Merchant | null> {
    return this.merchantRepository.findOneBy({ email });
  }
}
解决方案

一、全局异常过滤器(推荐方案)

完全可行,用NestJS的全局异常过滤器可以统一处理所有TypeORM抛出的23505错误,不用在每个服务里重复写捕获逻辑。

实现步骤:

  1. 创建全局异常过滤器类
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
import { Response } from 'express';
import { QueryFailedError } from 'typeorm';

@Catch(QueryFailedError)
export class UniqueConstraintFilter implements ExceptionFilter {
  catch(exception: QueryFailedError, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    
    // 处理PostgreSQL的唯一约束错误(错误码23505)
    if (exception.code === '23505') {
      const message = exception.detail.replace(
        /^Key \((.*)\)=\((.*)\) (.*)/,
        'The $1 $2 already exists.',
      );
      response.status(400).json({
        statusCode: 400,
        message: message,
        error: 'Bad Request'
      });
    } else {
      // 其他数据库错误,返回500
      response.status(500).json({
        statusCode: 500,
        message: 'Internal Server Error',
        error: 'Internal Server Error'
      });
    }
  }
}
  1. 在主模块(比如AppModule)注册全局过滤器
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { UniqueConstraintFilter } from './filters/unique-constraint.filter';

@Module({
  providers: [
    {
      provide: APP_FILTER,
      useClass: UniqueConstraintFilter,
    },
  ],
})
export class AppModule {}
  1. 移除服务中的重复代码
    现在可以删掉每个服务里捕获23505错误的逻辑,直接让异常抛出,过滤器会自动处理。比如你的MerchantsService可以简化成:
@Injectable()
export class MerchantsService {
  constructor(
    @InjectRepository(Merchant)
    private merchantRepository: Repository<Merchant>,
  ) {}

  public async create(createMerchantDto: CreateMerchantDto) {
    const user = this.merchantRepository.create({
      ...createMerchantDto,
      documentType: DocumentType.NIT,
      isActive: false,
    });
    await this.merchantRepository.save(user);

    const { password, ...merchantData } = createMerchantDto;

    return {
      ...merchantData,
    };
  }

  public async findOneByEmail(email: string): Promise<Merchant | null> {
    return this.merchantRepository.findOneBy({ email });
  }
}

二、备选方案:工具函数封装

如果因为某些原因不能用全局过滤器,工具函数是次优选择:

  1. 创建工具函数
// src/utils/handle-unique-error.ts
import { BadRequestException, InternalServerErrorException } from '@nestjs/common';
import { QueryFailedError } from 'typeorm';

export const handleUniqueError = (error: unknown) => {
  if (error instanceof QueryFailedError && error.code === '23505') {
    const message = error.detail.replace(
      /^Key \((.*)\)=\((.*)\) (.*)/,
      'The $1 $2 already exists.',
    );
    throw new BadRequestException(message);
  }
  throw new InternalServerErrorException();
};
  1. 在服务中导入使用
import { handleUniqueError } from '../utils/handle-unique-error';

// ...服务代码中
public async create(createMerchantDto: CreateMerchantDto) {
  try {
    // ...原有逻辑
  } catch (error) {
    handleUniqueError(error);
  }
}

方案对比

  • 全局过滤器:最优,完全解耦,无需在任何服务中重复处理,维护成本最低。
  • 工具函数:适合需要对部分错误做特殊处理的场景,但仍需在每个服务的catch块中调用,复用性不如全局过滤器。

内容的提问来源于stack exchange,提问作者Vicente Losada Fadul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 03:01:07