Your first migration
Now that you've defined a table, let's generate a migration to create it in the database.
Migrations in dibs are generated Rust files based on schema changes you make to your structs.
Generate the migration
Since you defined a User table but haven't created it in the database yet, dibs will detect this difference:
dibs diffYou should see output showing that the users table needs to be created.
Now generate a migration from that diff:
dibs generate-from-diff create-usersThis creates a new .rs file in crates/my-app-db/src/migrations/ with the SQL already written. It also prints the mod ...; line you need to add to migrations/mod.rs.
Set up the migrations module
Create crates/my-app-db/src/migrations/mod.rs:
pub mod m2026_01_24_120000_create_users; (Use the actual module name that generate-from-diff printed)
Then add this to crates/my-app-db/src/lib.rs:
pub mod migrations; The generated migration
The generated migration file looks like this:
use dibs::{ MigrationContext , MigrationResult };
# [ dibs :: migration ]
pub async fn migrate ( ctx : & mut MigrationContext < ' _ >) -> MigrationResult <()> {
ctx. execute ( "CREATE TABLE users (
id BIGINT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
display_name TEXT NOT NULL
)" ). await ?;
Ok (())
} Run the migration
dibs migrateThis applies the migration to your database. You can verify it worked with:
dibs statusAdding data migrations
Since migrations are Rust functions, you can add backfills, data transformations, or any logic you need:
# [ dibs :: migration ]
pub async fn migrate ( ctx : & mut MigrationContext < ' _ >) -> MigrationResult <()> {
// Schema change
ctx. execute ( "ALTER TABLE users ADD COLUMN bio TEXT" ). await ?;
// Data backfill
ctx. execute ( "UPDATE users SET bio = 'No bio yet' WHERE bio IS NULL" ). await ?;
Ok (())
} For large backfills, prefer batching so you don't lock tables:
loop {
let rows_affected = ctx. execute (
"UPDATE users SET bio = 'No bio yet'
WHERE bio IS NULL
LIMIT 1000"
). await ?;
if rows_affected == 0 {
break ;
}
} Running migrations
export DATABASE_URL =postgres://user:pass@localhost/mydb
dibs migrate
dibs statusCreating a blank migration
If you need a data-only migration (no schema changes), you can create an empty skeleton:
dibs generate backfill-user-data