Running Migrations
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'}');
}
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:
- Imports marker
- Registry marker
- buildMigrations()
New migrations are inserted between the import tags:
// <ORM-MIGRATION-IMPORTS>
// </ORM-MIGRATION-IMPORTS>
Each migration registers an id + migration instance:
final List<MigrationEntry> _entries = [
// <ORM-MIGRATION-REGISTRY>
// </ORM-MIGRATION-REGISTRY>
];
The CLI (and your code) asks the registry for descriptors:
/// Build migration descriptors sorted by timestamp.
List<MigrationDescriptor> buildMigrations() =>
MigrationEntry.buildDescriptors(_entries);
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