Migration & Seeding Events
Migrations and seeders emit structured events through the shared EventBus. Subscribe once to stream progress to logs, metrics, or tracing.
:::note Snippet context The snippets below are split into “subscribe” and “run” pieces so you can reuse the listener wiring in your own bootstrap code. :::
Migration Lifecycle
Events (all emitted by MigrationRunner):
MigrationBatchStartedEvent/MigrationBatchCompletedEventMigrationStartedEvent/MigrationCompletedEventMigrationFailedEvent(includeserrorandstackTrace)
Example listener + runner setup:
- Subscribe
- Run
bus.on<MigrationBatchStartedEvent>((event) {
print('Migration batch ${event.batch} started');
});
bus.on<MigrationStartedEvent>((event) {
print('Applying ${event.id}');
});
bus.on<MigrationCompletedEvent>((event) {
print('Applied ${event.id} in ${event.duration.inMilliseconds}ms');
});
bus.on<MigrationFailedEvent>((event) {
print('Failed ${event.id}: ${event.error}');
});
final descriptors = MigrationEntry.buildDescriptors([
MigrationEntry(
id: MigrationId.parse('m_20241201000000_create_users_table'),
migration: const CreateUsersTable(),
),
]);
final runner = MigrationRunner(
schemaDriver: schemaDriver,
ledger: SqlMigrationLedger(schemaDriver, tableName: '_orm_migrations'),
migrations: descriptors,
events: bus,
);
await runner.applyAll();
Seeding Lifecycle
Events (emitted by SeederRunner and database seeders):
SeedingStartedEvent/SeedingCompletedEventSeederStartedEvent/SeederCompletedEventSeederFailedEvent
Example with a custom seeder and event hooks:
Define a seeder
class DemoUserSeeder extends DatabaseSeeder {
DemoUserSeeder(super.connection);
Future<void> run() async {
await seed<User>([
{'name': 'Demo', 'email': 'demo@example.com'},
]);
}
}
- Subscribe
- Run
bus.on<SeedingStartedEvent>((event) {
print('Seeding started: ${event.seederNames.join(', ')}');
});
bus.on<SeederStartedEvent>((event) {
print('Running seeder ${event.seederName} (${event.index}/${event.total})');
});
bus.on<SeederCompletedEvent>((event) {
print('Seeder ${event.seederName} finished in ${event.duration}');
});
bus.on<SeederFailedEvent>((event) {
print('Seeder ${event.seederName} failed: ${event.error}');
});
bus.on<SeedingCompletedEvent>((event) {
print('Seeding complete (${event.count} seeders) in ${event.duration}');
});
final runner = SeederRunner(events: bus);
await runner.run(
connection: dataSource.connection,
seeders: const [
SeederRegistration(name: 'DemoUserSeeder', factory: DemoUserSeeder.new),
],
);
Tips:
- Use the same
EventBusfor migrations, seeders, and runtime queries to correlate logs. SeederFailedEventfires before the error is rethrown—log here, then let your process fail.- Combine with
pretend: trueonSeederRunner.runto capture SQL without mutating the database.
Verify Event Stream
- Run one migration batch and confirm batch + per-migration events are emitted.
- Run one seed flow and confirm start/completion/failure hooks.
- Confirm failed events include enough context for alerting.