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

在NestJS/TypeORM中检测重复值异常时code属性的TS类型

问题:NestJS + TypeORM + Postgres 唯一字段重复错误的TypeScript强类型处理

场景背景

我使用NestJS搭配TypeORM和Postgres数据库,已经为实体的username字段设置了唯一约束,实体代码如下:

@Entity()
export class DataEntity {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ unique: true })
  username: string;
  
  // 其他属性...
}

当前实现问题

向数据库添加数据时,需要在检测到重复username时返回错误提示。目前将catch中的ex设为any类型时代码可正常运行,但希望使用正确的TypeScript强类型。ex.code属性确实存在,但找不到包含该属性的对应类型。理想写法是if (ex instanceof WhateverError && ex.code === '23505'),但不清楚对应的错误类型。当前服务代码如下:

async createThing(data: dataDto): Promise<DataEntity> {

  try {
    // 数据库操作...
  } catch (ex) {
    console.log(ex);
    if (ex.code === '23505') {
      // 23505是Postgres检测到唯一约束冲突的错误码
      this.logger.error(`Username '${username}' already exists!`);
      throw new ConflictException(`Username '${username}' already exists!`);
    } else {
      this.logger.error(`Failed to create data! Data: ${JSON.stringify(createOrgDto)}`);
      throw new InternalServerErrorException();
    }
  }
}

错误日志详情

控制台输出的错误信息(已修改部分敏感内容):

QueryFailedError: duplicate key value violates unique constraint "UQ_e288cba8a890c81abc9dbac6b33"
    at PostgresQueryRunner.query (C:\Users\chris\myproj\src\driver\postgres\PostgresQueryRunner.ts:299:19)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at InsertQueryBuilder.execute (C:\Users\chris\myproj\src\query-builder\InsertQueryBuilder.ts:163:33)
    at SubjectExecutor.executeInsertOperations (C:\Users\chris\myproj\src\persistence\SubjectExecutor.ts:434:42)
    at SubjectExecutor.execute (C:\Users\chris\myproj\src\persistence\SubjectExecutor.ts:137:9)
    at EntityPersistExecutor.execute (C:\Users\chris\myproj\src\persistence\EntityPersistExecutor.ts:182:21)
    at DataService.createData (C:\Users\chris\myproj\src\data\data.service.ts:43:7) {
  query: 'INSERT INTO "Data"("id", "username", "password", "name", "logoUrl", "type", "superAdmin") VALUES (DEFAULT, $1, $2, $3, DEFAULT, DEFAULT, DEFAULT) RETURNING "id", "logoUrl", "type", "superAdmin"',
  parameters: [
    'foo',
    '<REDACTED_PASSWORD>',
    'FooCorp'
  ],
  driverError: error: duplicate key value violates unique constraint "UQ_e288cba8a890c81abc9dbac6b33"
      at C:\Users\chris\myproj\node_modules\pg\lib\client.js:526:17
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at PostgresQueryRunner.query (C:\Users\chris\myproj\src\driver\postgres\PostgresQueryRunner.ts:249:25)
      at InsertQueryBuilder.execute (C:\Users\chris\myproj\src\query-builder\InsertQueryBuilder.ts:163:33)
      at SubjectExecutor.executeInsertOperations (C:\Users\chris\myproj\src\persistence\SubjectExecutor.ts:434:42)
      at SubjectExecutor.execute (C:\Users\chris\myproj\src\persistence\SubjectExecutor.ts:137:9)
      at EntityPersistExecutor.execute (C:\Users\chris\myproj\src\persistence\EntityPersistExecutor.ts:182:21)
      at DataService.createData (C:\Users\chris\myproj\src\data\data.service.ts:43:7) {
    length: 249,
    severity: 'ERROR',
    code: '23505',
    detail: 'Key (username)=(foo) already exists.',
    hint: undefined,
    position: undefined,
    internalPosition: undefined,
    internalQuery: undefined,
    where: undefined,
    schema: 'public',
    table: 'Data',
    column: undefined,
    dataType: undefined,
    constraint: 'UQ_e288cba8a890c81abc9dbac6b33',
    file: 'nbtinsert.c',
    line: '666',
    routine: '_bt_check_unique'
  },
  length: 249,
  severity: 'ERROR',
  code: '23505',
  detail: 'Key (username)=(foo) already exists.',
  hint: undefined,
  position: undefined,
  internalPosition: undefined,
  internalQuery: undefined,
  where: undefined,
  schema: 'public',
  table: 'Data',
  column: undefined,
  dataType: undefined,
  constraint: 'UQ_e288cba8a890c81abc9dbac6b33',
  file: 'nbtinsert.c',
  line: '666',
  routine: '_bt_check_unique'
}

疑问

我已经尝试使用QueryFailedError类型,但它并没有code属性。难道只能强制使用any类型吗?


解决方案

1. 自定义类型守卫实现强类型判断

TypeORM的QueryFailedError本身未暴露code属性,但它包含的driverError字段是Postgres驱动抛出的错误对象,自带code属性。可以通过自定义类型守卫来实现强类型校验:

import { QueryFailedError } from 'typeorm';
import { PostgresError } from 'pg';

// 判断是否为Postgres唯一约束冲突错误
function isUniqueConstraintError(error: unknown): error is QueryFailedError & { driverError: PostgresError } {
  return (
    error instanceof QueryFailedError &&
    typeof (error as any).driverError === 'object' &&
    (error as any).driverError.code === '23505'
  );
}

在catch块中使用该守卫:

async createThing(data: dataDto): Promise<DataEntity> {
  try {
    // 执行数据库操作
    return await this.dataRepository.save(data);
  } catch (ex) {
    if (isUniqueConstraintError(ex)) {
      this.logger.error(`Username '${data.username}' already exists!`);
      throw new ConflictException(`Username '${data.username}' already exists!`);
    } else {
      this.logger.error(`Failed to create data! Data: ${JSON.stringify(data)}`);
      throw new InternalServerErrorException();
    }
  }
}

2. 扩展TypeORM的QueryFailedError类型声明

如果不想写类型守卫,可以通过扩展模块类型的方式,给QueryFailedError添加code属性的类型定义。在项目中新建typeorm.d.ts文件:

import { QueryFailedError } from 'typeorm';

declare module 'typeorm' {
  interface QueryFailedError {
    code: string;
    driverError: {
      code: string;
      detail: string;
      // 可按需添加其他需要的属性
    };
  }
}

添加后TypeScript会自动识别QueryFailedError上的code属性,无需额外类型转换。

3. 直接结合pg库的错误类型判断

Postgres驱动的错误类型在pg包的PostgresError中,可以直接结合它进行判断:

import { QueryFailedError } from 'typeorm';
import { PostgresError } from 'pg';

// 在catch块中
async createThing(data: dataDto): Promise<DataEntity> {
  try {
    // 数据库操作
  } catch (ex) {
    if (ex instanceof QueryFailedError && (ex.driverError as PostgresError).code === '23505') {
      this.logger.error(`Username '${data.username}' already exists!`);
      throw new ConflictException(`Username '${data.username}' already exists!`);
    } else {
      // 其他错误处理
      throw new InternalServerErrorException();
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 20:42:04