Migrations & Seed Data
Once your models are defined, you need to create the corresponding database tables and optionally populate them with initial data.
Prerequisites
What You’ll Learn
- How to generate migration files for tutorial entities
- How to define schema operations for each table
- How to seed deterministic starter data for development and tests
Step Outcome
By the end of this page, you should have:
- Repeatable schema history (timestamped migration files)
- Seeders that populate deterministic tutorial records
- A database that can be rebuilt from scratch with CLI commands
Why This Step Matters
- Migrations are schema history.
- Seeders are initial data fixtures.
- Keeping them separate lets you evolve schema and data independently.
Create migrations
Migrations allow you to evolve your database schema over time. You can generate migration files using the ormed CLI.
dart run ormed_cli:ormed make:migration --name create_genres --create --table genres
dart run ormed_cli:ormed make:migration --name create_movies --create --table movies
These commands generate Dart files in lib/src/database/migrations/. You can then define your columns using a fluent schema builder.
- Genres migration
- Movies migration
schema.create('genres', (table) {
table.id();
table.string('name');
table.text('description').nullable();
table.timestamps();
});
schema.create('movies', (table) {
table.id();
table.string('title');
table.integer('release_year');
table.text('summary').nullable();
table.string('poster_path').nullable();
table.integer('genre_id').nullable();
table.foreign(
['genre_id'],
references: 'genres',
referencedColumns: ['id'],
onDelete: ReferenceAction.setNull,
);
table.timestamps();
});
Seed with DTOs
Seeding is the process of populating your database with initial data, which is useful for development and testing. In Ormed, we use the generated DTOs to ensure our seed data matches our model definitions.
Individual Seeders
We create separate seeders for each model to keep things organized.
- Genre seeder
- Movie seeder
final repo = connection.context.repository<Genre>();
await repo.insertMany([
GenreInsertDto(
name: 'Drama',
description: 'Character-driven storytelling with emotional stakes.',
),
GenreInsertDto(
name: 'Science Fiction',
description: 'Speculative worlds, future tech, and big ideas.',
),
GenreInsertDto(
name: 'Mystery',
description: 'Twists, clues, and investigative tension.',
),
]);
final genres = await connection.query<Genre>().get();
final drama = genres.firstWhere((g) => g.name == 'Drama').id;
final sciFi = genres.firstWhere((g) => g.name == 'Science Fiction').id;
final mystery = genres.firstWhere((g) => g.name == 'Mystery').id;
await repo.insertMany([
MovieInsertDto(
title: 'City of Amber',
releaseYear: 2006,
summary: 'Two teens uncover the secrets of an underground city.',
genreId: sciFi,
),
MovieInsertDto(
title: 'Glass Letters',
releaseYear: 2019,
summary: 'A detective pieces together a locked-room mystery.',
genreId: mystery,
),
MovieInsertDto(
title: 'Ashes in Winter',
releaseYear: 2014,
summary: 'A family confronts loss and renewal after a storm.',
genreId: drama,
),
]);
// Demonstrate update DTOs (fix a typo in a summary).
await repo.update(
const MovieUpdateDto(
summary: 'A detective reconstructs a locked-room mystery.',
),
where: {'title': 'Glass Letters'},
);
The Root Seeder
The DatabaseSeeder is the entry point that coordinates all other seeders.
- Database seeder
await call([GenreSeeder.new, MovieSeeder.new]);
Run migrations + seeds
Finally, apply the migrations to create the tables and run the seeders to populate them.
# Apply all pending migrations
dart run ormed_cli:ormed migrate
# Run the database seeders
dart run ormed_cli:ormed seed
If you are iterating quickly and want a clean local state, run migrate/seed again after deleting the local SQLite file.