0
# Migration System
1
2
Database schema version control system for managing database structure changes, data migrations, and deployment workflows. TypeORM's migration system provides a robust way to evolve database schemas across environments.
3
4
## Capabilities
5
6
### Migration Interface
7
8
Core interface for implementing database migrations with up and down methods.
9
10
```typescript { .api }
11
/**
12
* Interface that all migration classes must implement
13
*/
14
interface MigrationInterface {
15
/**
16
* Applies the migration (forward direction)
17
* @param queryRunner - Query runner for database operations
18
* @returns Promise resolving when migration is applied
19
*/
20
up(queryRunner: QueryRunner): Promise<any>;
21
22
/**
23
* Reverts the migration (backward direction)
24
* @param queryRunner - Query runner for database operations
25
* @returns Promise resolving when migration is reverted
26
*/
27
down(queryRunner: QueryRunner): Promise<any>;
28
}
29
30
/**
31
* Migration metadata class
32
*/
33
class Migration {
34
/**
35
* Creates a new migration instance
36
* @param id - Migration ID (timestamp or number)
37
* @param timestamp - Migration timestamp
38
* @param name - Migration name
39
* @param instance - Migration implementation instance
40
*/
41
constructor(
42
id: number | string,
43
timestamp: number,
44
name: string,
45
instance?: MigrationInterface
46
);
47
48
/** Unique migration identifier */
49
readonly id: number | string;
50
51
/** Migration creation timestamp */
52
readonly timestamp: number;
53
54
/** Migration name/description */
55
readonly name: string;
56
57
/** Migration implementation instance */
58
readonly instance?: MigrationInterface;
59
}
60
```
61
62
**Migration Example:**
63
64
```typescript
65
import { MigrationInterface, QueryRunner } from "typeorm";
66
67
export class CreateUserTable1635123456789 implements MigrationInterface {
68
name = "CreateUserTable1635123456789";
69
70
public async up(queryRunner: QueryRunner): Promise<void> {
71
await queryRunner.query(`
72
CREATE TABLE "user" (
73
"id" SERIAL NOT NULL,
74
"name" character varying NOT NULL,
75
"email" character varying NOT NULL,
76
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
77
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
78
CONSTRAINT "UQ_USER_EMAIL" UNIQUE ("email"),
79
CONSTRAINT "PK_USER" PRIMARY KEY ("id")
80
)
81
`);
82
}
83
84
public async down(queryRunner: QueryRunner): Promise<void> {
85
await queryRunner.query(`DROP TABLE "user"`);
86
}
87
}
88
```
89
90
### Migration Executor
91
92
Service class for executing, reverting, and managing migrations in production environments.
93
94
```typescript { .api }
95
/**
96
* Executes and manages database migrations
97
*/
98
class MigrationExecutor {
99
/** Data source connection */
100
readonly connection: DataSource;
101
102
/**
103
* Executes all pending migrations
104
* @returns Promise resolving to array of executed migrations
105
*/
106
executePendingMigrations(): Promise<Migration[]>;
107
108
/**
109
* Reverts the most recent migration
110
* @returns Promise resolving when migration is reverted
111
*/
112
undoLastMigration(): Promise<void>;
113
114
/**
115
* Shows migration status (executed vs pending)
116
* @returns Promise resolving to boolean indicating if migrations are pending
117
*/
118
showMigrations(): Promise<boolean>;
119
120
/**
121
* Gets all migrations from files and database
122
* @returns Promise resolving to migrations array
123
*/
124
getAllMigrations(): Promise<Migration[]>;
125
126
/**
127
* Gets executed migrations from database
128
* @returns Promise resolving to executed migrations
129
*/
130
getExecutedMigrations(): Promise<Migration[]>;
131
132
/**
133
* Gets pending migrations that haven't been executed
134
* @returns Promise resolving to pending migrations
135
*/
136
getPendingMigrations(): Promise<Migration[]>;
137
138
/**
139
* Creates migrations table if it doesn't exist
140
* @returns Promise resolving when table is created
141
*/
142
createMigrationsTableIfNotExist(): Promise<void>;
143
144
/**
145
* Loads migration files from filesystem
146
* @returns Promise resolving to migration instances
147
*/
148
loadMigrations(): Promise<Migration[]>;
149
150
/**
151
* Executes a specific migration
152
* @param migration - Migration to execute
153
* @returns Promise resolving when migration is executed
154
*/
155
executeMigration(migration: Migration): Promise<void>;
156
157
/**
158
* Reverts a specific migration
159
* @param migration - Migration to revert
160
* @returns Promise resolving when migration is reverted
161
*/
162
undoMigration(migration: Migration): Promise<void>;
163
}
164
```
165
166
### Query Runner
167
168
Low-level interface for executing database operations during migrations.
169
170
```typescript { .api }
171
/**
172
* Query runner for database operations in migrations
173
*/
174
interface QueryRunner {
175
/** Data source connection */
176
connection: DataSource;
177
178
/** Current database connection */
179
databaseConnection: any;
180
181
/** Whether runner is released */
182
isReleased: boolean;
183
184
/** Whether runner is in transaction */
185
isTransactionActive: boolean;
186
187
/**
188
* Starts a new database transaction
189
* @returns Promise resolving when transaction is started
190
*/
191
startTransaction(): Promise<void>;
192
193
/**
194
* Commits current transaction
195
* @returns Promise resolving when transaction is committed
196
*/
197
commitTransaction(): Promise<void>;
198
199
/**
200
* Rolls back current transaction
201
* @returns Promise resolving when transaction is rolled back
202
*/
203
rollbackTransaction(): Promise<void>;
204
205
/**
206
* Executes raw SQL query
207
* @param query - SQL query string
208
* @param parameters - Query parameters
209
* @returns Promise resolving to query results
210
*/
211
query(query: string, parameters?: any[]): Promise<any>;
212
213
/**
214
* Creates a new database table
215
* @param table - Table definition
216
* @param ifNotExist - Whether to use IF NOT EXISTS clause
217
* @returns Promise resolving when table is created
218
*/
219
createTable(table: Table, ifNotExist?: boolean): Promise<void>;
220
221
/**
222
* Drops an existing database table
223
* @param tableOrName - Table instance or name
224
* @param ifExist - Whether to use IF EXISTS clause
225
* @returns Promise resolving when table is dropped
226
*/
227
dropTable(tableOrName: Table | string, ifExist?: boolean): Promise<void>;
228
229
/**
230
* Adds a new column to existing table
231
* @param tableOrName - Table instance or name
232
* @param column - Column definition
233
* @returns Promise resolving when column is added
234
*/
235
addColumn(tableOrName: Table | string, column: TableColumn): Promise<void>;
236
237
/**
238
* Drops column from existing table
239
* @param tableOrName - Table instance or name
240
* @param columnOrName - Column instance or name
241
* @returns Promise resolving when column is dropped
242
*/
243
dropColumn(tableOrName: Table | string, columnOrName: TableColumn | string): Promise<void>;
244
245
/**
246
* Creates a database index
247
* @param tableOrName - Table instance or name
248
* @param index - Index definition
249
* @returns Promise resolving when index is created
250
*/
251
createIndex(tableOrName: Table | string, index: TableIndex): Promise<void>;
252
253
/**
254
* Drops a database index
255
* @param tableOrName - Table instance or name
256
* @param indexOrName - Index instance or name
257
* @returns Promise resolving when index is dropped
258
*/
259
dropIndex(tableOrName: Table | string, indexOrName: TableIndex | string): Promise<void>;
260
261
/**
262
* Creates a foreign key constraint
263
* @param tableOrName - Table instance or name
264
* @param foreignKey - Foreign key definition
265
* @returns Promise resolving when foreign key is created
266
*/
267
createForeignKey(tableOrName: Table | string, foreignKey: TableForeignKey): Promise<void>;
268
269
/**
270
* Drops a foreign key constraint
271
* @param tableOrName - Table instance or name
272
* @param foreignKeyOrName - Foreign key instance or name
273
* @returns Promise resolving when foreign key is dropped
274
*/
275
dropForeignKey(tableOrName: Table | string, foreignKeyOrName: TableForeignKey | string): Promise<void>;
276
277
/**
278
* Releases the query runner and closes connection
279
* @returns Promise resolving when runner is released
280
*/
281
release(): Promise<void>;
282
}
283
```
284
285
### Schema Builder Classes
286
287
Classes for defining database schema elements in migrations.
288
289
```typescript { .api }
290
/**
291
* Database table definition for migrations
292
*/
293
class Table {
294
constructor(options: {
295
name: string;
296
columns: TableColumn[];
297
indices?: TableIndex[];
298
foreignKeys?: TableForeignKey[];
299
uniques?: TableUnique[];
300
checks?: TableCheck[];
301
exclusions?: TableExclusion[];
302
engine?: string;
303
database?: string;
304
schema?: string;
305
});
306
307
name: string;
308
columns: TableColumn[];
309
indices: TableIndex[];
310
foreignKeys: TableForeignKey[];
311
uniques: TableUnique[];
312
checks: TableCheck[];
313
exclusions: TableExclusion[];
314
}
315
316
/**
317
* Database column definition for migrations
318
*/
319
class TableColumn {
320
constructor(options: {
321
name: string;
322
type: ColumnType;
323
length?: string | number;
324
precision?: number;
325
scale?: number;
326
default?: any;
327
isNullable?: boolean;
328
isPrimary?: boolean;
329
isGenerated?: boolean;
330
generationStrategy?: "increment" | "uuid" | "rowid";
331
isUnique?: boolean;
332
comment?: string;
333
collation?: string;
334
charset?: string;
335
});
336
337
name: string;
338
type: ColumnType;
339
length?: string | number;
340
isNullable: boolean;
341
isPrimary: boolean;
342
default?: any;
343
}
344
345
/**
346
* Database index definition for migrations
347
*/
348
class TableIndex {
349
constructor(options: {
350
name?: string;
351
columnNames: string[];
352
isUnique?: boolean;
353
isSpatial?: boolean;
354
isFulltext?: boolean;
355
where?: string;
356
using?: string;
357
});
358
359
name?: string;
360
columnNames: string[];
361
isUnique: boolean;
362
}
363
364
/**
365
* Foreign key constraint definition for migrations
366
*/
367
class TableForeignKey {
368
constructor(options: {
369
name?: string;
370
columnNames: string[];
371
referencedTableName: string;
372
referencedColumnNames: string[];
373
onDelete?: "RESTRICT" | "CASCADE" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
374
onUpdate?: "RESTRICT" | "CASCADE" | "SET NULL" | "SET DEFAULT" | "NO ACTION";
375
});
376
377
name?: string;
378
columnNames: string[];
379
referencedTableName: string;
380
referencedColumnNames: string[];
381
onDelete?: string;
382
onUpdate?: string;
383
}
384
```
385
386
**Schema Building Example:**
387
388
```typescript
389
import { MigrationInterface, QueryRunner, Table, TableColumn, TableIndex, TableForeignKey } from "typeorm";
390
391
export class CreatePostsTable1635123456790 implements MigrationInterface {
392
public async up(queryRunner: QueryRunner): Promise<void> {
393
await queryRunner.createTable(
394
new Table({
395
name: "post",
396
columns: [
397
new TableColumn({
398
name: "id",
399
type: "int",
400
isPrimary: true,
401
isGenerated: true,
402
generationStrategy: "increment"
403
}),
404
new TableColumn({
405
name: "title",
406
type: "varchar",
407
length: "255"
408
}),
409
new TableColumn({
410
name: "content",
411
type: "text"
412
}),
413
new TableColumn({
414
name: "author_id",
415
type: "int"
416
}),
417
new TableColumn({
418
name: "published_at",
419
type: "timestamp",
420
isNullable: true
421
}),
422
new TableColumn({
423
name: "created_at",
424
type: "timestamp",
425
default: "CURRENT_TIMESTAMP"
426
})
427
]
428
})
429
);
430
431
await queryRunner.createIndex(
432
"post",
433
new TableIndex({
434
name: "IDX_POST_TITLE",
435
columnNames: ["title"]
436
})
437
);
438
439
await queryRunner.createForeignKey(
440
"post",
441
new TableForeignKey({
442
columnNames: ["author_id"],
443
referencedTableName: "user",
444
referencedColumnNames: ["id"],
445
onDelete: "CASCADE"
446
})
447
);
448
}
449
450
public async down(queryRunner: QueryRunner): Promise<void> {
451
await queryRunner.dropTable("post");
452
}
453
}
454
```
455
456
## Migration Commands
457
458
TypeORM provides CLI commands for migration management:
459
460
### CLI Commands
461
462
```bash
463
# Generate a new migration based on entity changes
464
typeorm migration:generate -n CreateUserTable
465
466
# Create an empty migration file
467
typeorm migration:create -n AddIndexToUser
468
469
# Run pending migrations
470
typeorm migration:run
471
472
# Revert last migration
473
typeorm migration:revert
474
475
# Show migration status
476
typeorm migration:show
477
```
478
479
### Programmatic Usage
480
481
```typescript
482
// Execute migrations programmatically
483
const dataSource = new DataSource({
484
// connection options
485
migrations: ["src/migrations/*.ts"],
486
migrationsRun: true // Automatically run migrations on startup
487
});
488
489
await dataSource.initialize();
490
491
// Manual migration execution
492
await dataSource.runMigrations();
493
494
// Revert migrations
495
await dataSource.undoLastMigration();
496
497
// Check pending migrations
498
const pendingMigrations = await dataSource.showMigrations();
499
```
500
501
## Migration Best Practices
502
503
### Safe Migration Patterns
504
505
```typescript
506
export class SafeColumnAddition1635123456791 implements MigrationInterface {
507
public async up(queryRunner: QueryRunner): Promise<void> {
508
// Add nullable column first
509
await queryRunner.addColumn("user", new TableColumn({
510
name: "phone",
511
type: "varchar",
512
length: "20",
513
isNullable: true
514
}));
515
516
// Populate data if needed
517
await queryRunner.query(`
518
UPDATE "user" SET "phone" = '' WHERE "phone" IS NULL
519
`);
520
521
// Make column non-nullable if required
522
await queryRunner.changeColumn("user", "phone", new TableColumn({
523
name: "phone",
524
type: "varchar",
525
length: "20",
526
isNullable: false,
527
default: "''"
528
}));
529
}
530
531
public async down(queryRunner: QueryRunner): Promise<void> {
532
await queryRunner.dropColumn("user", "phone");
533
}
534
}
535
```
536
537
### Data Migrations
538
539
```typescript
540
export class MigrateUserData1635123456792 implements MigrationInterface {
541
public async up(queryRunner: QueryRunner): Promise<void> {
542
// Get all users
543
const users = await queryRunner.query(`SELECT * FROM "user"`);
544
545
// Process data in batches
546
for (let i = 0; i < users.length; i += 100) {
547
const batch = users.slice(i, i + 100);
548
549
for (const user of batch) {
550
// Transform and update data
551
const transformedData = transformUserData(user);
552
await queryRunner.query(
553
`UPDATE "user" SET "new_field" = $1 WHERE "id" = $2`,
554
[transformedData.newField, user.id]
555
);
556
}
557
}
558
}
559
560
public async down(queryRunner: QueryRunner): Promise<void> {
561
// Revert data changes
562
await queryRunner.query(`UPDATE "user" SET "new_field" = NULL`);
563
}
564
}