MasterRecord
Migrations CLI Reference
The
masterrecord binary and its master db wrappers — dotnet ef for Node.The CLI ships with the masterrecord package (npx masterrecord <command>). Run it from the project root that contains your context; every command takes the context file name (e.g. AppContext → resolves *AppContext.js). Inside a Master app, master db <action> runs the same commands against AppContext with the right environment, and creates the snapshot on first use.
Global options#
| Option | Effect | EF Core |
|---|---|---|
--connection <json> | A JSON connection config (optionally keyed by context name) overriding the environment file for this run. Honoured by context.env() via MASTERRECORD_CONNECTION_OVERRIDE. | --connection |
-v, --version | Print the version. | — |
terminal
$ masterrecord update-database AppContext --connection '{"type":"sqlite","connection":"./tmp/"}'
Environment
The context reads
config/environments/env.<env>.json, selected by the NODE_ENV (or master) environment variable. There is no --env flag on masterrecord itself — set the variable (NODE_ENV=production npx masterrecord …) or use master db … --env production, which sets it for you. Without it the CLI fails with “No environment specified”.Command reference#
| Command (alias) | Does | EF Core |
|---|---|---|
enable-migrations <ctx> (em) | One-time: creates an empty <ctx>_contextSnapShot.json next to the context. The snapshot records the last authored model, so it starts from nothing and advances with each add-migration. | (implicit in migrations add) |
add-migration <Name> <ctx> (am) | Diffs the current models against the snapshot, writes <timestamp>_<Name>_migration.js with up()/down(), then advances the snapshot — so the next add-migration diffs against this one. Prints a // POSSIBLE RENAME advisory when a drop+add pair looks like a rename. | dotnet ef migrations add |
update-database <ctx> (ud) | Applies every pending migration in timestamp order, each atomically with its row in _masterrecord_migrations; re-running is a no-op. Auto-creates a missing MySQL/Postgres database. | dotnet ef database update |
update-database-down <ctx> (udd) | Runs the down() of the most recently applied migration and removes its tracking row. | database update <previous> |
update-database-target <migrationFile> (udt) | Rolls back (down()) every migration newer than the named file, newest first. Legacy command — not tracking-table aware; prefer repeated update-database-down. | database update <Name> |
update-database-restart <ctx> (udr) | Re-runs every migration file’s up() from the first — a dev-database reset; not tracking-table aware. | — |
remove-migration <ctx> (rm) [-f, --force] | Deletes the latest migration file. Refuses an applied migration unless --force, which reverts it first (its down(), in its own transaction) and points the snapshot’s latestMigration at the previous one. A pending file is simply deleted. | dotnet ef migrations remove |
migrations-status <ctx> (ms) | Applied migrations with timestamps, pending ones, recorded-but-missing files, and the snapshot’s latest migration. | dotnet ef migrations list |
script <ctx> [-o, --output <file>] | Prints the SQL update-database would run for pending migrations — DDL plus the tracking insert, per migration — without applying anything. Introspection still runs against the live database so the plan is accurate. | dotnet ef migrations script |
get-migrations <ctx> (gm) | Lists the migration files for a context. | (file listing) |
ensure-database <ctx> (ed) | Creates the target database if it is missing (MySQL / Postgres). | Database.EnsureCreated() (database only) |
enable-migrations-all (ema) | Enables migrations for every *Context.js found in the project. | — |
add-migration-all <Name> (ama) | Creates a migration with the given name for every detected context. | — |
update-database-all (uda) | Runs update-database for every context: all pending migrations, recorded, per-context summary, isolated connections, non-zero exit if any context fails. | database update --context "*" (EF 11) |
Typical session#
terminal
$ masterrecord enable-migrations AppContext$ masterrecord add-migration InitialCreate AppContext$ masterrecord migrations-status AppContext$ masterrecord script AppContext -o review.sql$ masterrecord update-database AppContext$ # oops — undo the last one$ masterrecord update-database-down AppContext$ masterrecord remove-migration AppContext
The master db wrappers#
master db always targets AppContext, runs inside the backend folder, sets NODE_ENV (--env, default development), and runs enable-migrations automatically when no snapshot exists yet — so master db new / migrate just work on a fresh app, as dotnet ef migrations add does.
master db | Runs | Options |
|---|---|---|
migrate | masterrecord update-database AppContext | --env |
rollback | masterrecord update-database-down AppContext | --env |
new <Name> | masterrecord add-migration <Name> AppContext | --env |
remove | masterrecord remove-migration AppContext | -f, --force, --env |
status | masterrecord migrations-status AppContext | --env |
script | masterrecord script AppContext | -o, --output <file>, --env |
enable | masterrecord enable-migrations AppContext | --env |
list | masterrecord get-migrations AppContext | --env |
ensure | masterrecord ensure-database AppContext | --env |
terminal
$ master db new AddViewsToPost$ master db script -o review.sql$ master db migrate --env production$ master db status$ master db remove --force
How applying works#
- Tracking table. Applied migrations are recorded in
_masterrecord_migrations;update-databaseapplies what is pending (every file not in the table, in timestamp order) and is idempotent. - Atomic per migration. Postgres and SQLite wrap each migration’s DDL and its tracking row in one transaction, so a failing migration leaves nothing half-applied. MySQL DDL implicitly commits, so it runs as before (EF documents the same).
- Snapshot = last authored model.
<ctx>_contextSnapShot.jsonrecords the model as of the newest migration file plus itslatestMigrationid. Only the authoring commands write it (add-migration,enable-migrations,remove-migration); applying never does, exactly asdotnet ef database updatenever touchesModelSnapshot. That is what keeps every migration a delta between authored states, so a fresh database replays them all. Commit it: two branches that each add a migration conflict on merge, surfacing a divergent migration tree. - FK constraints are added after tables exist (
schema.finalize()after eachup()/down()), so creation order never matters.
package.json
{
"scripts": {
"migrate": "masterrecord update-database-all",
"migrate:plan": "masterrecord script AppContext"
}
}