Migration & Seeding Events
Migrations and seeders emit structured events through the shared EventBus. Subscribe once to stream progress to logs, metrics, or tracing.
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.