Database Migrations
April 22, 2026 ยท View on GitHub
This guide collects the ZinTrust migration workflow in one place.
If you are looking for the CLI command to add a column to an existing table, use:
zin add migration [column] [model]
Example:
zin add migration bio user
Shorthand:
zin am bio user
This generates a timestamped migration file under database/migrations/ with a logical name like add_bio_users_table.
zin s vs migration commands
zin s starts the application in development mode. It does not create or run migrations.
Migration-related commands are separate:
zin create migration [model]
zin add migration [column] [model]
zin migrate
Migration Generator Commands
Create a table migration
Use this when the table does not exist yet:
zin create migration user
Shorthand:
zin cm user
This generates a migration named like create_users_table.
Add a column migration
Use this when the table already has a create migration:
zin add migration bio user
Shorthand:
zin am bio user
This generates a migration named like add_bio_users_table.
Important:
- ZinTrust expects the model's create-table migration to exist first.
- If it cannot find a matching
create_<models>_tablemigration, it will stop and tell you to create it first.
Custom migration names
For advanced cases, you can generate a migration with a custom logical name:
zin add migration create_users_table
zin add migration backfill_user_flags
Use the custom form when the opinionated add-column naming does not fit the change you need.
Typical Workflow
1. Create the table migration
zin cm user
2. Add follow-up column migrations as needed
zin am bio user
zin am avatar_url user
zin am status user
3. Edit the generated file
Generated migrations are only scaffolds. You still need to fill in the schema logic inside up() and down().
Example:
import { Blueprint, type IDatabase, MigrationSchema } from '@zintrust/core';
export interface Migration {
up(db: IDatabase): Promise<void>;
down(db: IDatabase): Promise<void>;
}
export const migration: Migration = {
async up(db: IDatabase): Promise<void> {
const schema = MigrationSchema.create(db);
await schema.table('users', (table: Blueprint) => {
table.string('bio').nullable();
});
},
async down(db: IDatabase): Promise<void> {
const schema = MigrationSchema.create(db);
await schema.table('users', (table: Blueprint) => {
table.dropColumn('bio');
});
},
};
Column Helpers
Use the migration blueprint helper that matches the value you need to store:
table.date('blocked_date')for a date-only column such as2026-04-22table.timestamp('blocked_at')for a date-time column
Example:
await schema.table('users', (table: Blueprint) => {
table.date('blocked_date').nullable();
table.timestamp('blocked_at').nullable();
});
For your specific case, the right migration API is:
table.date('blocked_date');
Running Migrations
Run pending migrations:
zin migrate
Useful variants:
zin migrate --status
zin migrate --rollback
zin migrate --rollback --step 2
zin migrate --fresh
zin migrate --reset
Cloudflare D1
If your project uses Cloudflare D1, use the D1-aware migration mode:
zin migrate --local --database zintrust_db
Or remote:
zin migrate --remote --database zintrust_db
Where Migration Files Live
By default, ZinTrust reads migrations from:
database/migrations
That default comes from the database config migration directory.
Troubleshooting
"Usage: zin add migration [column] [model]"
You passed the add-column form with missing arguments. Use both the column name and the model name:
zin add migration bio user
"Missing required create migration for model"
Create the table migration first:
zin create migration user
I expected zin s to handle migrations
zin s only starts the app. Migration generation and execution are separate CLI commands.