Prisma Next is in early access.Read the docs

The migration graph

You changed your schema, a teammate changed theirs, both merged. The migration graph is what lets every database catch up without renaming files or rebuilding history.

You add a phone column on your branch. A teammate adds an avatarUrl column on theirs. Both branches merge the same afternoon. Now your laptop, your teammate's laptop, staging, and production are each sitting on a slightly different version of the schema, and something has to bring every one of them up to the merged state without losing work or running the same change twice.

Classic migration tools make this hard. They keep migrations in a single folder, run in timestamp order, exactly once. When two branches each add a migration, the timestamps interleave, the "run once in order" assumption breaks, and someone spends an afternoon renaming files to force a clean line. The migration graph is how Prisma Next avoids that.

When this matters

You can build a whole app without thinking about the graph. It earns its place the moment more than one line of history exists:

  • Two people (or two AI agents) change the schema on separate branches and merge.
  • You need to roll a database back to an earlier schema and then forward again.
  • A database is behind (a fresh clone, a long-lived staging box) and has to catch up through several changes.
  • You want to point an environment at an exact schema state and prove in review that it is there.

If none of these apply yet, read How migrations work instead and come back when they do.

The short version

Prisma Next does not treat migrations as one timestamp-ordered list. Each migration records the schema state it starts from and the schema state it produces, and those links form a graph. A database moves through the graph by following migrations from wherever it currently sits to wherever you tell it to go. Because every migration is anchored to real schema states rather than to its position in a folder, branches, merges, and rollbacks are all just paths through the same graph.

Terms used on this page

TermMeaning
ContractThe schema you author, and the contract.json artifact it compiles to.
Contract stateOne exact shape of the schema at a point in history.
HashA short fingerprint of a contract state, like sha256:705b1a6…, the way a Git commit hash names an exact state of your files.
NodeA contract state in the graph, identified by its hash.
EdgeA migration. It moves the database from one node (from) to another (to).
MarkerA record Prisma Next keeps inside the database naming the one node it currently matches.
RefA human-readable name for a node, like prod, stored as a file in your repo.

How it works

Every time you emit your contract, you get a deterministic JSON artifact. Hashing it produces an identifier for that exact schema shape. Those hashes are the nodes of the graph.

A migration is an edge. It records the contract hash it starts from and the hash it moves the database to. Nothing about a migration's place in history comes from its file name: the timestamps in directory names are for humans, and the real linkage lives in the from/to hashes inside each migration.json.

States are nodes, migrations are edgesStep 1 of 3
initalice_phonebob_avatarmergemergeemptynew database705b1a6after init93be6c2Alice's branch7e3fa7fBob's branchf9a41d7merged
Every emitted contract hashes to an identifier for that exact schema state. A migration is an edge from one state to the next.

A database always sits at exactly one node. Its marker, a record Prisma Next keeps in the database itself, holds the hash of the contract it currently matches. Applying a migration means walking one edge and moving the marker.

A worked example

Here is the Alice-and-Bob situation as an actual graph. Alice adds phone, Bob adds avatarUrl, and both branches merge:

npx prisma-next migration graph
*     f9a41d7  (prod)
|-\
|^|   20260303T1000_merge_alice          93be6c2 -> f9a41d7  1 ops
| |^  20260303T1100_merge_bob            7e3fa7f -> f9a41d7  1 ops
* |   93be6c2
|^|   20260302T1000_alice_add_phone      705b1a6 -> 93be6c2  1 ops
| *   7e3fa7f
| |^  20260302T1100_bob_add_avatar_url   705b1a6 -> 7e3fa7f  1 ops
|-/
*     705b1a6
|^    20260301T1000_init                       - -> 705b1a6  1 ops
*     -

1 space(s), 5 contract(s), 5 migration(s)

Read it bottom-up. From an empty database (-), init establishes state 705b1a6. Alice and Bob both branch from 705b1a6: her migration produces 93be6c2, his produces 7e3fa7f. Each branch then has a merge migration into the combined state f9a41d7, which the prod ref points at. This shape is a diamond: one start, two parallel branches, one shared end.

The payoff is in the last column and the two merge edges. A database that followed Alice's branch is sitting at 93be6c2 and reaches f9a41d7 through merge_alice. A database that followed Bob's is at 7e3fa7f and takes merge_bob. Neither developer had to know about the other's migration, and no files were renamed to make the timestamps line up.

(The summary line counts contract spaces: independent migration lanes, one for your app plus one per database extension.)

What happens when you run migrate

When you run prisma-next migrate, the runner walks the graph for you:

  1. It reads the database's marker to find which node the database is on now.
  2. It resolves your target: the merged state, a ref like prod, or the latest state if you did not pass --to.
  3. It finds a path of edges from the marker to the target.
  4. It applies each edge in order, moving the marker after each one.
  5. If any migration's precheck fails, it stops before running SQL and names the mismatch.

If two branch tips are both reachable and neither is clearly the target, the runner does not guess. It stops and asks you to pick one with --to.

Inspecting the graph

Four read-only commands, each answering a different question:

Question you haveCommandNeeds a database?
What does the whole topology look like?migration graphNo
Which migration directories exist on disk?migration listNo
Where is my database, and what is pending?migration statusYes
What has actually been applied, and when?migration logYes

migration graph also takes --json for machine-readable output, --dot to render with Graphviz, and --legend to print a key for the glyphs. migration log reads a ledger the runner appends to on every apply, so even a rollback shows up as history rather than as deleted history.

Try it on real fixtures

The Prisma Next repo ships example graphs (a diamond, a wide fan-out, converging branches, rollback chains) as ready-to-render fixtures:

git clone https://github.com/prisma/prisma-next
cd prisma-next && pnpm install && pnpm -w build && pnpm install
cd examples/prisma-next-demo
npx prisma-next migration graph --config fixtures/showcase/prisma-next.config.ts --legend

Swap showcase for diamond, wide-fan, converging-branches, multi-branch, long-spine, or skip-rollback to explore each shape.

Name important states with refs

Raw hashes are awkward to type and impossible to remember, so name the states that matter. A ref is a human-readable pointer to a node, stored as a small file in migrations/app/refs/ and committed to your repo:

npx prisma-next ref set prod sha256:f9a41d7...
npx prisma-next ref list
npx prisma-next migrate --to prod

Because refs are committed, "where production should be" is a reviewable fact in a pull request, not tribal knowledge in someone's terminal history.

One ref name is special. A ref called db is the default starting point migration plan uses when you do not pass --from. Keep it current with prisma-next migrate --advance-ref db and planning stays incremental on its own.

Two reserved tokens also work anywhere a command accepts a contract reference, without setting up a ref:

  • @contract: the contract you most recently emitted.
  • @db: whatever state the connected database's marker holds.

What the graph gives you

Parallel work without ordering conflicts

Alice and Bob each planned a migration from 705b1a6 on their own branches. In a timestamp-ordered system, whoever merges second inherits a broken history. Here, both edges are in the graph and every environment finds its own path to the merged state. This matters twice over when the "two developers" are two AI agents planning migrations concurrently, since neither has to know about the other.

History you can trust

Because each edge declares its from state, a migration can never silently run against a database in the wrong shape. If the marker does not match, the run stops before any SQL executes, with an error naming the mismatch.

Rollback as a normal move

An edge can point backwards, from a later contract state to an earlier one. Rolling back is not a special mode: it is planning one more migration whose destination is a state you have already been in. See Rollbacks and recovery.

More than one shape of history

The graph accommodates whatever your workflow produces: long linear spines, wide fan-outs where many branches leave one node, diamonds that converge again, and fast-forward edges that jump several states in one hop.

Database-specific details

The graph model is family-neutral: same nodes, same edges, same commands on every database. The parts that differ are in how the marker and ledger are stored.

  • PostgreSQL: the marker lives in a prisma_contract.marker table, and each apply runs inside a transaction guarded by an advisory lock so two runners cannot move the same database at once.
  • MongoDB: the marker and ledger live in a _prisma_migrations collection, updated with a compare-and-swap so a concurrent apply is rejected rather than interleaved.

Early-access limitations

The graph, pathfinding, refs, and the marker/ledger model all work today. A few things you might reach for do not exist yet:

  • No squash. You cannot yet collapse a long chain of migrations into one. The chain stays as-is.
  • No baseline. You cannot yet adopt an existing database's history as a starting node without replaying it.
  • No split. You cannot yet break one large migration into smaller ones after the fact.
  • Ambiguous targets need a choice. When two branch tips are both reachable, migrate asks you to pick one with --to rather than guessing.

The graph was designed with these in mind; the commands simply have not shipped. Each migrations page calls out what is missing rather than papering over it.

Common tasks

TaskCommand
See the whole graphprisma-next migration graph
Check where a database isprisma-next migration status
Move a database to a named stateprisma-next migrate --to prod
Name the current state prodprisma-next ref set prod @db
List the refs you have namedprisma-next ref list
Roll a database back one stateprisma-next migrate --to <earlier-ref-or-hash>
Verify migration files and graph integrityprisma-next migration check

Prompt your coding agent

Projects scaffolded with create-prisma@next install Prisma Next skills for your coding agent. Ask your agent to:

  • "Draw the migration graph for this project and explain the branches."
  • "Which contract state is the prod ref pointing at, and is the database there yet?"
  • "Two feature branches both added migrations. Check whether they conflict."

See also

On this page