Skip to main content

Running Migrations

:::note Snippet context These snippets are split by step (entries → ledger → runner → apply/rollback) so you can copy just the part you need. :::

Programmatic Usage

Use MigrationRunner to run migrations in code:

1) Define entries

final entries = [
MigrationEntry(
id: MigrationId.parse('m_20241201000000_create_users_table'),
migration: const CreateUsersTable(),
),
MigrationEntry(
id: MigrationId.parse('m_20241201000100_create_posts_table'),
migration: const CreatePostsTable(),
),
];

// Build descriptors (sorted by timestamp)
final descriptors = MigrationEntry.buildDescriptors(entries);

2) Create a ledger

// Create ledger to track applied migrations
final ledger = SqlMigrationLedger(driver, tableName: '_orm_migrations');
await ledger.ensureInitialized();

3) Create the runner

// Create runner
final runner = MigrationRunner(
schemaDriver: driver,
ledger: ledger,
migrations: descriptors,
);

4) Apply / rollback

// Apply all pending migrations
await runner.applyAll();

// Or apply with a limit
await runner.applyAll(limit: 5);
// Rollback last batch
await runner.rollback();

// Rollback multiple batches
await runner.rollback(steps: 3);

5) Inspect status

// Check status
final statuses = await runner.status();
for (final status in statuses) {
print('${status.id}: ${status.isApplied ? 'Applied' : 'Pending'}');
}

CLI Workflow (Most Common)

dart run ormed_cli:ormed migrate
dart run ormed_cli:ormed migrate --pretend
dart run ormed_cli:ormed migrate:rollback --steps 1
dart run ormed_cli:ormed migrate:status

Use --pretend before risky deploys to inspect generated SQL without applying changes.

Ledger API

The ledger tracks which migrations have been applied:

Future<void> ledgerApiExample(DriverAdapter driver) async {
final ledger = SqlMigrationLedger(driver, tableName: 'orm_migrations');
await ledger.ensureInitialized();

// Get next batch number
final batch = await ledger.nextBatchNumber();

// Log applied migration
// await ledger.logApplied(
// descriptor,
// DateTime.now().toUtc(),
// batch: batch,
// );

// Using ConnectionManager
// final managedLedger = SqlMigrationLedger.managed(
// connectionName: 'primary',
// tableName: 'orm_migrations',
// );
// await managedLedger.ensureInitialized();
}

Migration Registry

The CLI maintains a registry file to track available migrations:

Each migration registers an id + migration instance:

final List<MigrationEntry> _entries = [
// <ORM-MIGRATION-REGISTRY>
// </ORM-MIGRATION-REGISTRY>
];

The marker comments allow the CLI to automatically insert new migrations.

Troubleshooting

Migration Already Applied

Error: Migration X has already been applied

Use migrate:status to check applied migrations. Roll back first if you need to re-run.

Checksum Mismatch

Error: Migration checksum doesn't match recorded value

A migration was modified after being applied. Either:

  • Revert changes to the migration file
  • Create a new migration with the changes

Foreign Key Constraint Failed

Ensure:

  • Parent tables are created before child tables
  • Child tables are dropped before parent tables
  • Foreign key columns match the referenced column type

Deployment Tip

Run migration status checks in CI/CD before and after applying migrations so drift is visible immediately.

Read This Next