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

如何在TypeORM迁移出错时自动执行down方法回滚?

在NestJS+TypeORM中实现迁移失败自动回滚

我们在NestJS项目中使用TypeORM做数据库迁移,现有迁移脚本示例如下:

import {MigrationInterface, QueryRunner} from "typeorm";

export class VMTestTableTwo1713851442445 implements MigrationInterface {

    public async up(queryRunner: QueryRunner): Promise<void> {
      await queryRunner.query(`
        CREATE TABLE "vm_test"(
          "aggregate_id" uuid NOT NULL
        );
      `);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
      await queryRunner.query(`
        DROP TABLE IF EXISTS "vm_test";
      `);
    }
}

理想状态下,若up脚本执行失败,应当自动执行down脚本完成回滚,但TypeORM迁移默认不支持该特性(Sequelize迁移具备此功能),请问如何在TypeORM中实现这一功能?

更新补充TypeORM配置:

export default TypeOrmModule.forRootAsync({
  useFactory: (config: ConfigService) => {
    return {
      type: 'postgres',
      host: <host>,
      port: <port>,
      username: <username>,
      password: <pwd>,
      database: <db>,
      synchronize: false,
      logging: EnvChecker.queryLogger() ? ['query', 'error'] : false,
      migrationsRun: true,
      migrations: [
        `${__dirname}/../migrations/*{.ts,.js}`
      ],
      autoLoadEntities: true,
      entities: [
        `${__dirname}/../**/domain/entities/*.entity{.ts,.js}`
      ],
      extra: {
        max: <maxPool>,
        connectionTimeoutMillis: <connectionTimeoutMillis>,
        idleTimeoutMillis: <config.get('idleTimeoutMillis')>
      },
      migrationsTransactionMode: 'each'
    };
  },
  inject: [ConfigService]
});

解决方案

1. 利用事务自动回滚(基础方案)

你配置里已经设置了migrationsTransactionMode: 'each',这个配置会让TypeORM为每个迁移开启独立事务:

  • 当up脚本执行过程中抛出错误,事务会自动回滚,撤销up中已执行的所有操作,效果等价于自动执行down的核心逻辑(但不会主动调用down方法)。
  • 这种方式无需修改迁移脚本,依赖数据库事务的原子性保证,适用于绝大多数常规迁移场景。

注意:如果迁移操作包含数据库不支持事务的语句(比如PostgreSQL的CREATE INDEX CONCURRENTLY),事务会失效,这种情况需要使用下面的自定义方案。

2. 自定义迁移基类封装回滚逻辑(进阶方案)

如果需要严格触发down方法完成回滚(比如迁移中包含非事务性操作),可以创建一个通用迁移基类,在up执行失败时主动调用down:

import { MigrationInterface, QueryRunner } from "typeorm";

export abstract class BaseMigration implements MigrationInterface {
  abstract up(queryRunner: QueryRunner): Promise<void>;
  abstract down(queryRunner: QueryRunner): Promise<void>;

  public async runUp(queryRunner: QueryRunner): Promise<void> {
    try {
      await this.up(queryRunner);
    } catch (error) {
      // 捕获up执行错误,调用down回滚
      await this.down(queryRunner);
      // 重新抛出错误,让TypeORM标记迁移失败
      throw error;
    }
  }
}

随后让你的迁移类继承该基类,覆盖默认执行逻辑:

import { QueryRunner } from "typeorm";
import { BaseMigration } from "./base.migration";

export class VMTestTableTwo1713851442445 extends BaseMigration {
    public async up(queryRunner: QueryRunner): Promise<void> {
      await queryRunner.query(`
        CREATE TABLE "vm_test"(
          "aggregate_id" uuid NOT NULL
        );
      `);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
      await queryRunner.query(`
        DROP TABLE IF EXISTS "vm_test";
      `);
    }

    // 复用基类的回滚逻辑
    public async runUp(queryRunner: QueryRunner): Promise<void> {
      return super.runUp(queryRunner);
    }
}

3. 命令行手动回滚(应急方案)

如果上述方案无法覆盖你的场景,当迁移失败后,可以手动执行TypeORM回滚命令触发down:

# 回滚最后一次执行的迁移
npx typeorm-ts-node-esm migration:revert

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 13:28:23