Migrations
Tango migrations keep the database schema aligned with your model metadata.
In a typical application, tango.config.ts sits next to the migration workflow. It describes the database target and migration directory once, and the tango CLI reuses that config when it runs migration commands.
The migration package supports three related jobs:
- describing schema changes with a class-based migration API
- generating migration files from model metadata and database introspection
- applying migrations in a controlled order with a journal table
The migration API surface
@danceroutine/tango-migrations gives application code these main pieces:
Migrationop,OpBuilder, andCollectingBuilderMigrationRunnerMigrationGeneratordiffSchema- SQL compilers and introspectors for PostgreSQL and SQLite
The tango executable now lives in @danceroutine/tango-cli, which composes the migration commands from this package into the shared Tango CLI.
Migration classes
Every migration subclasses Migration and defines:
idup(m)down(m)
The example from the package README is accurate for the current code:
export default class CreatePosts extends Migration {
id = '20260302_create_posts';
up(m) {
m.run(
op.table('posts').create((cols) => {
cols.add('id', (b) => b.serial().primaryKey());
cols.add('title', (b) => b.string().notNull());
})
);
}
down(m) {
m.run(op.table('posts').drop());
}
}How generated migrations work
diffSchema() compares model metadata with an introspected database schema. It can generate operations for:
- create table
- drop table
- add column
- drop column
- create index
- drop index
The generator then writes those operations into a migration file.
MigrationGenerator.generate() throws if there are no operations to write, which prevents empty migration files from being created.
How migrations are applied
MigrationRunner reads files from a directory, sorts them, skips already applied migrations, and records applied migrations in _tango_migrations.
MigrationRunner also:
- stores a checksum for each applied migration
- wraps non-online PostgreSQL migrations in a transaction
- supports
apply(),plan(), andstatus()
How the CLI integrates with migrations
@danceroutine/tango-migrations exports registerMigrationsCommands(), and @danceroutine/tango-cli mounts that command tree into the shared tango binary.
When ./tango.config.ts exists, the CLI can infer the dialect, database target, and migration directory from that file. Explicit flags still win when you need a one-off override.
In day-to-day application work, this is the command surface you use:
tango migratetango make:migrationstango plantango status
The example applications call the CLI through package scripts or pnpm exec rather than assuming a global install.
Practical advice
Treat migration generation as the start of review, not the end of it.
You still need to inspect the generated operations, especially if:
- a model or field was renamed
- an index disappeared
- a relation changed
- the migration wants to drop a table or column you expected to keep