Migrations
- How migrations work
- Creating a new migration
- Running and reverting migrations
- Generating migrations
- Connection option
- Using migration API to write migrations
How migrations work
Once you get into production you'll need to synchronize model changes into the database.
Typically it is unsafe to use synchronize: true
for schema synchronization on production once
you get data in your database. Here is where migrations come to help.
A migration is just a single file with sql queries to update a database schema and apply new changes to an existing database.
Let's say you already have a database and a post entity:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
text: string;
}
And your entity worked in production for months without any changes. You have thousands of posts in your database.
Now you need to make a new release and rename title
to name
.
What would you do?
You need to create a new migration with the following sql query (postgres dialect):
ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name";
Once you run this sql query your database schema is ready to work with your new codebase. TypeORM provides a place where you can write such sql queries and run them when needed. This place is called "migrations".
Creating a new migration
Pre-requisites: Installing CLI
Before creating a new migration you need to setup your connection options properly:
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "test",
"password": "test",
"database": "test",
"entities": ["entity/*.js"],
"migrationsTableName": "custom_migration_table",
"migrations": ["migration/*.js"],
"cli": {
"migrationsDir": "migration"
}
}
Here we setup three options:
* "migrationsTableName": "migrations"
- Specify this option only if you need migration table name to be different from "migrations"
.
* "migrations": ["migration/*.js"]
- indicates that typeorm must load migrations from the given "migration" directory.
* "cli": { "migrationsDir": "migration" }
- indicates that the CLI must create new migrations in the "migration" directory.
Once you setup connection options you can create a new migration using CLI:
typeorm migration:create -n PostRefactoring
Here, PostRefactoring
is the name of the migration - you can specify any name you want.
After you run the command you can see a new file generated in the "migration" directory
named {TIMESTAMP}-PostRefactoring.ts
where {TIMESTAMP}
is the current timestamp when the migration was generated.
Now you can open the file and add your migration sql queries there.
You should see the following content inside your migration:
import {MigrationInterface, QueryRunner} from "typeorm";
export class PostRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
}
async down(queryRunner: QueryRunner): Promise<void> {
}
}
There are two methods you must fill with your migration code: up
and down
.
up
has to contain the code you need to perform the migration.
down
has to revert whatever up
changed.
down
method is used to revert the last migration.
Inside both up
and down
you have a QueryRunner
object.
All database operations are executed using this object.
Learn more about query runner.
Let's see what the migration looks like with our Post
changes:
import {MigrationInterface, QueryRunner} from "typeorm";
export class PostRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "title" TO "name"`);
}
async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "post" RENAME COLUMN "name" TO "title"`); // reverts things made in "up" method
}
}
Running and reverting migrations
Once you have a migration to run on production, you can run them using a CLI command:
typeorm migration:run
typeorm migration:create
and typeorm migration:generate
will create .ts
files, unless you use the o
flag (see more in Generating migrations). The migration:run
and migration:revert
commands only work on .js
files. Thus the typescript files need to be compiled before running the commands. Alternatively you can use ts-node
in conjunction with typeorm
to run .ts
migration files.
Example with ts-node
:
ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run
Example ts-node
not using node_modules
directly:
ts-node $(yarn bin typeorm) migration:run
This command will execute all pending migrations and run them in a sequence ordered by their timestamps.
This means all sql queries written in the up
methods of your created migrations will be executed.
That's all! Now you have your database schema up-to-date.
If for some reason you want to revert the changes, you can run:
typeorm migration:revert
This command will execute down
in the latest executed migration.
If you need to revert multiple migrations you must call this command multiple times.
Generating migrations
TypeORM is able to automatically generate migration files with schema changes you made.
Let's say you have a Post
entity with a title
column, and you have changed the name title
to name
.
You can run following command:
typeorm migration:generate -n PostRefactoring
And it will generate a new migration called {TIMESTAMP}-PostRefactoring.ts
with the following content:
import {MigrationInterface, QueryRunner} from "typeorm";
export class PostRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`);
}
async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "name" RENAME TO "title"`);
}
}
Alternatively you can also output your migrations as Javascript files using the o
(alias for --outputJs
) flag. This is useful for Javascript only projects in which TypeScript additional packages are not installed. This command, will generate a new migration file {TIMESTAMP}-PostRefactoring.js
with the following content:
const { MigrationInterface, QueryRunner } = require("typeorm");
module.exports = class PostRefactoringTIMESTAMP {
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "post" ALTER COLUMN "title" RENAME TO "name"`);
}
}
See, you don't need to write the queries on your own.
The rule of thumb for generating migrations is that you generate them after each change you made to your models. To apply multi-line formatting to your generated migration queries, use the p
(alias for --pretty
) flag.
Connection option
If you need to run/revert your migrations for another connection rather than the default, use the -c
(alias for --connection
) and pass the config name as an argument
typeorm -c <your-config-name> migration:{run|revert}
Using migration API to write migrations
In order to use an API to change a database schema you can use QueryRunner
.
Example:
import {MigrationInterface, QueryRunner, Table, TableIndex, TableColumn, TableForeignKey } from "typeorm";
export class QuestionRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(new Table({
name: "question",
columns: [
{
name: "id",
type: "int",
isPrimary: true
},
{
name: "name",
type: "varchar",
}
]
}), true)
await queryRunner.createIndex("question", new TableIndex({
name: "IDX_QUESTION_NAME",
columnNames: ["name"]
}));
await queryRunner.createTable(new Table({
name: "answer",
columns: [
{
name: "id",
type: "int",
isPrimary: true
},
{
name: "name",
type: "varchar",
},
{
name: 'created_at',
type: 'timestamp',
default: 'now()'
}
]
}), true);
await queryRunner.addColumn("answer", new TableColumn({
name: "questionId",
type: "int"
}));
await queryRunner.createForeignKey("answer", new TableForeignKey({
columnNames: ["questionId"],
referencedColumnNames: ["id"],
referencedTableName: "question",
onDelete: "CASCADE"
}));
}
async down(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable("answer");
const foreignKey = table.foreignKeys.find(fk => fk.columnNames.indexOf("questionId") !== -1);
await queryRunner.dropForeignKey("answer", foreignKey);
await queryRunner.dropColumn("answer", "questionId");
await queryRunner.dropTable("answer");
await queryRunner.dropIndex("question", "IDX_QUESTION_NAME");
await queryRunner.dropTable("question");
}
}
getDatabases(): Promise<string[]>
Returns all available database names including system databases.
getSchemas(database?: string): Promise<string[]>
database
- If database parameter specified, returns schemas of that database
Returns all available schema names including system schemas. Useful for SQLServer and Postgres only.
getTable(tableName: string): Promise<Table|undefined>
tableName
- name of a table to be loaded
Loads a table by a given name from the database.
getTables(tableNames: string[]): Promise<Table[]>
tableNames
- name of a tables to be loaded
Loads a tables by a given names from the database.
hasDatabase(database: string): Promise<boolean>
database
- name of a database to be checked
Checks if database with the given name exist.
hasSchema(schema: string): Promise<boolean>
schema
- name of a schema to be checked
Checks if schema with the given name exist. Used only for SqlServer and Postgres.
hasTable(table: Table|string): Promise<boolean>
table
- Table object or name
Checks if table exist.
hasColumn(table: Table|string, columnName: string): Promise<boolean>
table
- Table object or namecolumnName
- name of a column to be checked
Checks if column exist in the table.
createDatabase(database: string, ifNotExist?: boolean): Promise<void>
database
- database nameifNotExist
- skips creation iftrue
, otherwise throws error if database already exist
Creates a new database.
dropDatabase(database: string, ifExist?: boolean): Promise<void>
database
- database nameifExist
- skips deletion iftrue
, otherwise throws error if database was not found
Drops database.
createSchema(schemaPath: string, ifNotExist?: boolean): Promise<void>
schemaPath
- schema name. For SqlServer can accept schema path (e.g. 'dbName.schemaName') as parameter. If schema path passed, it will create schema in specified databaseifNotExist
- skips creation iftrue
, otherwise throws error if schema already exist
Creates a new table schema.
dropSchema(schemaPath: string, ifExist?: boolean, isCascade?: boolean): Promise<void>
schemaPath
- schema name. For SqlServer can accept schema path (e.g. 'dbName.schemaName') as parameter. If schema path passed, it will drop schema in specified databaseifExist
- skips deletion iftrue
, otherwise throws error if schema was not foundisCascade
- Iftrue
, automatically drop objects (tables, functions, etc.) that are contained in the schema. Used only in Postgres.
Drops a table schema.
createTable(table: Table, ifNotExist?: boolean, createForeignKeys?: boolean, createIndices?: boolean): Promise<void>
table
- Table object.ifNotExist
- skips creation iftrue
, otherwise throws error if table already exist. Defaultfalse
createForeignKeys
- indicates whether foreign keys will be created on table creation. Defaulttrue
createIndices
- indicates whether indices will be created on table creation. Defaulttrue
Creates a new table.
dropTable(table: Table|string, ifExist?: boolean, dropForeignKeys?: boolean, dropIndices?: boolean): Promise<void>
table
- Table object or table name to be droppedifExist
- skips dropping iftrue
, otherwise throws error if table does not existdropForeignKeys
- indicates whether foreign keys will be dropped on table deletion. Defaulttrue
dropIndices
- indicates whether indices will be dropped on table deletion. Defaulttrue
Drops a table.
renameTable(oldTableOrName: Table|string, newTableName: string): Promise<void>
oldTableOrName
- old Table object or name to be renamednewTableName
- new table name
Renames a table.
addColumn(table: Table|string, column: TableColumn): Promise<void>
table
- Table object or namecolumn
- new column
Adds a new column.
addColumns(table: Table|string, columns: TableColumn[]): Promise<void>
table
- Table object or namecolumns
- new columns
Adds a new column.
renameColumn(table: Table|string, oldColumnOrName: TableColumn|string, newColumnOrName: TableColumn|string): Promise<void>
table
- Table object or nameoldColumnOrName
- old column. Accepts TableColumn object or column namenewColumnOrName
- new column. Accepts TableColumn object or column name
Renames a column.
changeColumn(table: Table|string, oldColumn: TableColumn|string, newColumn: TableColumn): Promise<void>
table
- Table object or nameoldColumn
- old column. Accepts TableColumn object or column namenewColumn
- new column. Accepts TableColumn object
Changes a column in the table.
changeColumns(table: Table|string, changedColumns: { oldColumn: TableColumn, newColumn: TableColumn }[]): Promise<void>
table
- Table object or namechangedColumns
- array of changed columns.oldColumn
- old TableColumn objectnewColumn
- new TableColumn object
Changes a columns in the table.
dropColumn(table: Table|string, column: TableColumn|string): Promise<void>
table
- Table object or namecolumn
- TableColumn object or column name to be dropped
Drops a column in the table.
dropColumns(table: Table|string, columns: TableColumn[]|string[]): Promise<void>
table
- Table object or namecolumns
- array of TableColumn objects or column names to be dropped
Drops a columns in the table.
createPrimaryKey(table: Table|string, columnNames: string[]): Promise<void>
table
- Table object or namecolumnNames
- array of column names which will be primary
Creates a new primary key.
updatePrimaryKeys(table: Table|string, columns: TableColumn[]): Promise<void>
table
- Table object or namecolumns
- array of TableColumn objects which will be updated
Updates composite primary keys.
dropPrimaryKey(table: Table|string): Promise<void>
table
- Table object or name
Drops a primary key.
createUniqueConstraint(table: Table|string, uniqueConstraint: TableUnique): Promise<void>
table
- Table object or nameuniqueConstraint
- TableUnique object to be created
Creates new unique constraint.
Note: does not work for MySQL, because MySQL stores unique constraints as unique indices. Use
createIndex()
method instead.
createUniqueConstraints(table: Table|string, uniqueConstraints: TableUnique[]): Promise<void>
table
- Table object or nameuniqueConstraints
- array of TableUnique objects to be created
Creates new unique constraints.
Note: does not work for MySQL, because MySQL stores unique constraints as unique indices. Use
createIndices()
method instead.
dropUniqueConstraint(table: Table|string, uniqueOrName: TableUnique|string): Promise<void>
table
- Table object or nameuniqueOrName
- TableUnique object or unique constraint name to be dropped
Drops an unique constraint.
Note: does not work for MySQL, because MySQL stores unique constraints as unique indices. Use
dropIndex()
method instead.
dropUniqueConstraints(table: Table|string, uniqueConstraints: TableUnique[]): Promise<void>
table
- Table object or nameuniqueConstraints
- array of TableUnique objects to be dropped
Drops an unique constraints.
Note: does not work for MySQL, because MySQL stores unique constraints as unique indices. Use
dropIndices()
method instead.
createCheckConstraint(table: Table|string, checkConstraint: TableCheck): Promise<void>
table
- Table object or namecheckConstraint
- TableCheck object
Creates new check constraint.
Note: MySQL does not support check constraints.
createCheckConstraints(table: Table|string, checkConstraints: TableCheck[]): Promise<void>
table
- Table object or namecheckConstraints
- array of TableCheck objects
Creates new check constraint.
Note: MySQL does not support check constraints.
dropCheckConstraint(table: Table|string, checkOrName: TableCheck|string): Promise<void>
table
- Table object or namecheckOrName
- TableCheck object or check constraint name
Drops check constraint.
Note: MySQL does not support check constraints.
dropCheckConstraints(table: Table|string, checkConstraints: TableCheck[]): Promise<void>
table
- Table object or namecheckConstraints
- array of TableCheck objects
Drops check constraints.
Note: MySQL does not support check constraints.
createForeignKey(table: Table|string, foreignKey: TableForeignKey): Promise<void>
table
- Table object or nameforeignKey
- TableForeignKey object
Creates a new foreign key.
createForeignKeys(table: Table|string, foreignKeys: TableForeignKey[]): Promise<void>
table
- Table object or nameforeignKeys
- array of TableForeignKey objects
Creates a new foreign keys.
dropForeignKey(table: Table|string, foreignKeyOrName: TableForeignKey|string): Promise<void>
table
- Table object or nameforeignKeyOrName
- TableForeignKey object or foreign key name
Drops a foreign key.
dropForeignKeys(table: Table|string, foreignKeys: TableForeignKey[]): Promise<void>
table
- Table object or nameforeignKeys
- array of TableForeignKey objects
Drops a foreign keys.
createIndex(table: Table|string, index: TableIndex): Promise<void>
table
- Table object or nameindex
- TableIndex object
Creates a new index.
createIndices(table: Table|string, indices: TableIndex[]): Promise<void>
table
- Table object or nameindices
- array of TableIndex objects
Creates a new indices.
dropIndex(table: Table|string, index: TableIndex|string): Promise<void>
table
- Table object or nameindex
- TableIndex object or index name
Drops an index.
dropIndices(table: Table|string, indices: TableIndex[]): Promise<void>
table
- Table object or nameindices
- array of TableIndex objects
Drops an indices.
clearTable(tableName: string): Promise<void>
tableName
- table name
Clears all table contents.
Note: this operation uses SQL's TRUNCATE query which cannot be reverted in transactions.
enableSqlMemory(): void
Enables special query runner mode in which sql queries won't be executed, instead they will be memorized into a special variable inside query runner.
You can get memorized sql using getMemorySql()
method.
disableSqlMemory(): void
Disables special query runner mode in which sql queries won't be executed. Previously memorized sql will be flushed.
clearSqlMemory(): void
Flushes all memorized sql statements.
getMemorySql(): SqlInMemory
- returns
SqlInMemory
object with array ofupQueries
anddownQueries
sql statements
Gets sql stored in the memory. Parameters in the sql are already replaced.
executeMemoryUpSql(): Promise<void>
Executes memorized up sql queries.
executeMemoryDownSql(): Promise<void>
Executes memorized down sql queries.