Migrations
orm.migrate() reads your TypeScript interface files, generates a schema, diffs it against the current database, and applies the minimal set of DDL statements needed. No separate migration files to write.
orm.migrate()
// orm.migrate() — reads TypeScript source files,
// generates schema, diffs against current DB state, applies DDL
import ORMManager from 'slintorm';
const orm = new ORMManager({ driver: 'sqlite', databaseUrl: './dev.db', dir: './src' });
// Run once on startup or in a migration script
await orm.migrate();
// -> scans ./src for interface definitions
// -> generates schema/generated.ts + schema/generated.json
// -> creates/alters tables as neededAuto-injected columns
// Columns injected automatically on every table:
// createdAt TEXT — set on INSERT
// updatedAt TEXT — set on INSERT and UPDATE
// deletedAt TEXT — ONLY when a field has @softDelete
// (tables without @softDelete get no extra column)Alter table (non-destructive)
// Alter table — non-destructive
// Adding a field to an interface adds the column
// Removing a field from an interface removes the column
// Before:
interface User {
id: number;
email: string;
name: string;
}
// After (add bio, remove name):
interface User {
id: number;
email: string;
// @nullable
bio: string;
}
// migrate() runs:
// ALTER TABLE users ADD COLUMN bio TEXT
// ALTER TABLE users DROP COLUMN name (Postgres/MySQL)
// On SQLite: recreate table (limitations apply)_slint_migrations tracking table
SlintORM creates this table automatically. Each row records a migration batch, model name, and a SHA-256 hash. If the hash hasn't changed, that model's migration is skipped.
// _slint_migrations table — created automatically
-- SQLite / MySQL schema:
CREATE TABLE IF NOT EXISTS _slint_migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
batch INTEGER NOT NULL,
hash TEXT NOT NULL,
snapshot TEXT,
migratedAt TEXT NOT NULL
);
-- Postgres schema:
CREATE TABLE IF NOT EXISTS _slint_migrations (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
batch INTEGER NOT NULL,
hash TEXT NOT NULL,
snapshot TEXT,
migratedAt TEXT NOT NULL
);Migration hooks
// MigrationRunHooks — observe migration progress
await orm.migrate({
onPending(units) {
console.log(`${units.length} migrations pending`);
},
onProgress(unit) {
console.log(`Running: ${unit.name}`);
},
onDone(result) {
console.log(`Done. ${result.ran} migrations applied.`);
},
onError(unit, err) {
console.error(`Failed: ${unit.name}`, err);
},
});Schema snapshots
// Schema snapshots
// Saved to: src/schema/migrations/_schema_snapshots/batch-N/
// Each batch captures a JSON snapshot of all model schemas at that point
// hashSchemaModel — SHA-256 of stable JSON
// If the hash matches the last recorded hash, no migration runs for that model
// Change an interface -> hash changes -> migration runsMigrationUnit / MigrationRunResult types
// MigrationUnit — represents one pending migration
interface MigrationUnit {
name: string; // model/table name
hash: string; // SHA-256 of schema JSON
sql: string[]; // DDL statements to execute
batch: number; // batch number
}
// MigrationRunResult — returned by orm.migrate()
interface MigrationRunResult {
ran: number; // number of migrations applied
skipped: number; // number skipped (hash matched)
batch: number; // batch number used
}Snapshot-based architecture
The migration system records field snapshots (not step-based diffs). This means if you change an existing model field's type or constraints without running fresh, the change may be silently skipped if the hash still matches. Use npx slintorm fresh to reset and re-run all migrations in development.