Track and apply database schema changes with migrations
Migrations are versioned scripts that describe how your database schema should change over time. Each migration has an up function that applies the change and a down function that reverts it, letting you move forward and backward through your schema history in a controlled, repeatable way.
sequelize.sync() inspects your model definitions and attempts to bring the database schema in line with them. This is convenient during early development but has significant drawbacks in production:
It cannot safely modify columns or rename tables without data loss.
It gives you no history of what changed, when, or why.
Rolling back a bad change requires manual intervention.
Running sync({ force: true }) drops and recreates tables, destroying all data.
Migrations solve these problems by treating every schema change as an explicit, reviewable, reversible commit to your database structure.
Avoid using sequelize.sync() in production. Use migrations to manage all schema changes on databases that hold real data.
The CLI will create a file in your configured migrationFolder (default: ./migrations). The filename includes a timestamp prefix so that migrations are applied in creation order:
migrations/└── 20240315143022-create-users.ts
For the sql format, a directory is created instead of a single file, containing two SQL files:
queryInterface exposes the full set of DDL operations supported by Sequelize. The examples below cover the operations you will use most often.
In migration files, import DataTypes from @sequelize/core or use sequelize.dialect.DataTypes (the sequelize parameter passed to up/down). The snippets below use the import form for brevity.
Sequelize tracks which migrations have been applied in a table called SequelizeMeta (created automatically on first run). Each row stores the filename of a migration that has been successfully executed.
SequelizeMeta┌─────────────────────────────────────────┐│ name │├─────────────────────────────────────────┤│ 20240101000000-create-users.ts ││ 20240201000000-add-email-verified-at.ts │└─────────────────────────────────────────┘
When you run migrations, Sequelize reads all files in your migrationFolder, compares them against SequelizeMeta, and runs only the ones that are not yet recorded. This ensures migrations are never applied twice.
Do not modify or delete rows from SequelizeMeta manually. If you need to re-run a migration, revert it first using the appropriate down path.
Wrapping your migration in a transaction ensures that if any step fails, all previous steps in that migration are rolled back automatically, leaving the database in its prior consistent state.
Not all databases support DDL statements inside transactions. PostgreSQL does; MySQL and MariaDB do not roll back DDL changes on error. Check your database’s documentation.
Each migration should do one logical thing — create a table, add a column, or add an index. Smaller migrations are easier to review, easier to roll back, and less likely to cause conflicts when multiple developers are working in parallel.
Once a migration has been applied to any environment (staging, production), treat it as immutable. Create a new migration to make further changes. Modifying a migration that has already run will cause a mismatch between the SequelizeMeta record and the actual database state.