Skip to content

typeorm/no-changecolumn-recreate ​

Reports queryRunner.changeColumn() calls that make TypeORM drop and re-add the column, which deletes every value in it.

CategoryDefault severityDatabases
data-losserrorPostgreSQL, MySQL

What happens ​

changeColumn() does not always alter a column in place. When the change needs a conversion, TypeORM drops the column and adds it again ("To avoid data conversion, we just recreate column"), and every existing value is lost.

  • PostgreSQL: when the type, length, or array flag changes, when the column becomes a stored generated column, or when the expression of a stored generated column changes.
  • MySQL: when the type or length changes, when the generated type changes, or when generation is turned on or off (except for uuid).

orm-preflight compares the old and new TableColumn. When the old column is passed by name, as a string, it cannot compare them, and reports a warning instead of an error.

A changeColumn() that only renames is reported by no-rename-column, and one that sets isNullable: false by no-set-not-null.

Bad ​

ts
import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm'

export class WidenUserName1727200000000 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.changeColumn(
      'users',
      new TableColumn({ name: 'name', type: 'varchar', length: '100' }),
      new TableColumn({ name: 'name', type: 'varchar', length: '255' }),
    )
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    // ...
  }
}

Safe ​

Write the change as SQL, which alters the column in place:

ts
import { MigrationInterface, QueryRunner } from 'typeorm'

export class WidenUserName1727200000000 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "name" TYPE character varying(255)`)
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    // ...
  }
}

A longer varchar, or varchar to text, keeps the data and does not rewrite the table on PostgreSQL. For other type changes, use expand and contract: add a new column, backfill it, switch the code, and drop the old column in a later release.

When to suppress ​

When losing the column's data is intended, or the table is known to be empty in every environment.

ts
// preflight safety-assured typeorm/no-changecolumn-recreate -- column holds derived data, recomputed by the job

References ​