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#

OptionEffectEF 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, --versionPrint 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)DoesEF 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 dbRunsOptions
migratemasterrecord update-database AppContext--env
rollbackmasterrecord update-database-down AppContext--env
new <Name>masterrecord add-migration <Name> AppContext--env
removemasterrecord remove-migration AppContext-f, --force, --env
statusmasterrecord migrations-status AppContext--env
scriptmasterrecord script AppContext-o, --output <file>, --env
enablemasterrecord enable-migrations AppContext--env
listmasterrecord get-migrations AppContext--env
ensuremasterrecord 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-database applies 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.json records the model as of the newest migration file plus its latestMigration id. Only the authoring commands write it (add-migration, enable-migrations, remove-migration); applying never does, exactly as dotnet ef database update never touches ModelSnapshot. 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 each up()/down()), so creation order never matters.
package.json
{
  "scripts": {
    "migrate": "masterrecord update-database-all",
    "migrate:plan": "masterrecord script AppContext"
  }
}