# How migrations work (/docs/orm/migrations/how-migrations-work)

> For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

Change your contract, plan a migration, review it, apply it. Operations can check the database before and after they run.

Location: ORM > Migrations > How migrations work

A migration is how Prisma ORM changes your database when your contract changes. Your contract is the `contract.prisma` file that replaced `schema.prisma`, and `npx prisma orm init` puts it in `src/prisma/`. After you edit it, run `npx prisma contract emit`, which replaces `prisma generate`. Next to `contract.prisma` it writes the files the rest of Prisma ORM reads: `contract.json`, which the migration commands read, and `contract.d.ts`, which your client's types come from.

Prisma ORM 8 supports PostgreSQL and MongoDB. SQLite is experimental, and MySQL is not supported, as [Supported databases](https://www.prisma.io/docs/orm/supported-databases) lists.

You run this loop many times a day:

<ConceptAnimation name="migration-loop" />

1. **Change your contract**: edit `contract.prisma`, then run `npx prisma contract emit`.
2. **Plan a migration**: `migration plan` works out what has to change in the database by comparing your new contract with an earlier contract state, and writes what it finds as a migration directory under `migrations/app/`. A contract state is one version of your contract, named by its hash, so it is how Prisma ORM refers to your contract as it stood at a particular moment. Unless you tell it otherwise, the earlier state it compares against is the `db` ref, the file `migrations/app/refs/db.json` naming the contract state you last applied in development.
3. **Review it**: before anything touches the database, run `npx prisma migration show <dir>` to see the operations and SQL that `db migrate` will run. If you want the migration to change rows as well, edit `migration.ts` as [Editing a migration](https://www.prisma.io/docs/orm/migrations/editing-a-migration) shows, and then recompile it by running `node migrations/app/<dir>/migration.ts` from your project root, which rewrites `ops.json` and `migration.json` from your edits without needing a database connection. You need nothing extra for that, because the Prisma ORM CLI already requires Node.js 22.18 or later, which runs `migration.ts` directly.
4. **Apply it**: `db migrate` starts by reading the database's marker, the record in the database of which contract state it matches, so it knows how much of your history the database has already seen, and then runs the migrations from that state to your current contract. In development, add `--advance-ref db`, which points the `db` ref at the state you just applied, so the next `migration plan` starts from the state your database actually matches.

With a database connection set up, as in the [quickstart](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql), add an optional `phone String?` field to `User`, then run:

  

#### bun

```bash
bunx prisma contract emit
bunx prisma migration plan --name add_user_phone
bunx prisma migration show 20260707T1006_add_user_phone
bunx prisma db migrate --advance-ref db
```

#### pnpm

```bash
pnpm dlx prisma contract emit
pnpm dlx prisma migration plan --name add_user_phone
pnpm dlx prisma migration show 20260707T1006_add_user_phone
pnpm dlx prisma db migrate --advance-ref db
```

#### yarn

```bash
yarn dlx prisma contract emit
yarn dlx prisma migration plan --name add_user_phone
yarn dlx prisma migration show 20260707T1006_add_user_phone
yarn dlx prisma db migrate --advance-ref db
```

#### npm

```bash
npx prisma contract emit
npx prisma migration plan --name add_user_phone
npx prisma migration show 20260707T1006_add_user_phone
npx prisma db migrate --advance-ref db
```

`migration plan` prepares the SQL but doesn't run it:

```text
✔ Planned 1 operation(s)

migrations/app/20260707T1006_add_user_phone
└─ Add column "phone" to "user"

from:       705b1a62f26f0913caa4bfe3f8b7cb491a1b94bd47fc43471d8711bc480bcbb5
to:         925198f3cc272c5fd19c24ac02f251661775ddac21cdac4e634bbc0dda8b2d72
app space:  migrations/app/20260707T1006_add_user_phone

ℹ DDL preview

ALTER TABLE "public"."user" ADD COLUMN "phone" text;
```

A table name is the model name with a lowercase first letter unless the model sets `@@map`, so `User` becomes `"user"` and `UserProfile` becomes `"userProfile"`. If your database was created by Prisma ORM 7, it has a `"User"` table instead, so [the upgrade guide](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql#24-edit-the-inferred-contract) maps the old names with `@@map` before you plan a migration for it.

The `app space:` line tells you which migration history the new directory was written into. A contract space is a separate migration history, so your own migrations stay in `migrations/app/` while each [Prisma ORM extension package](https://www.prisma.io/docs/orm/migrations/applying-a-migration#extension-spaces) that ships migrations, such as pgvector, keeps its migrations in its own directory under `migrations/`.

`db migrate` applies the migration and confirms what it applied:

```text
✔ Applied 1 migration(s) (1 operation(s)) across 1 contract space(s)
```

The first time you run `migration plan`, there are no migrations and no `db` ref on disk yet, so there is no earlier state to compare against and Prisma ORM plans as if the database were empty. If you want it to start somewhere else, pass `--from`, such as `--from 20260707T1006_add_user_phone` for the state after that migration. The [`migration plan` reference](https://www.prisma.io/docs/cli/migration-plan#options) lists the other forms `--from` accepts.

If you apply a migration without `--advance-ref db`, or plan a second migration before you apply the first, the `db` ref still names the older contract state, so the next `migration plan` compares against that older state and plans changes you have already planned once. [The db ref](https://www.prisma.io/docs/orm/migrations/generating-a-migration#the-db-ref-skipping---from) shows how to delete the duplicate migration.

`db migrate` needs a database to talk to, and it takes that from `db.connection` in [`prisma.config.ts`](https://www.prisma.io/docs/orm/contract-authoring/the-data-contract), such as `db: { connection: process.env["DATABASE_URL"]! }`, unless you pass a URL with the `--db` flag. In CI and production, run plain `npx prisma db migrate`.

## What a migration contains [#what-a-migration-contains]

A migration directory is named with a timestamp and the `--name` you passed. `migrations/` is in the directory you run commands from, normally your project root:

```text
migrations/
├── app/
│   └── 20260707T1006_add_user_phone/
│       ├── migration.ts     # the change, as TypeScript
│       ├── ops.json         # the operations, as JSON
│       └── migration.json   # where this migration fits in history
└── snapshots/
    └── <contract hash>/
        ├── contract.json    # snapshot of one contract state
        └── contract.d.ts    # types for that snapshot; they type-check migration.ts
```

After `migration plan`, commit the new migration directory, any new directories under `migrations/snapshots/`, `contract.prisma`, `contract.json`, and `contract.d.ts`.

### migration.ts: the file you edit [#migrationts-the-file-you-edit]

`migration plan` writes the change as `migration.ts`, a TypeScript class with one call per step, such as `this.addColumn(...)` for a new column, so you can read and change it like any other TypeScript file. [Editing a migration](https://www.prisma.io/docs/orm/migrations/editing-a-migration) explains how to change it.

### ops.json: the file Prisma runs [#opsjson-the-file-prisma-runs]

`migration plan` also writes the same operations as JSON in `ops.json`, and `ops.json` is what `db migrate` actually runs, never `migration.ts`. That is why recompiling matters: if you edit `migration.ts` and don't recompile it, `db migrate` runs the old `ops.json` rather than your edit, and neither it nor `migration check` warns you.

### migration.json: its place in history [#migrationjson-the-history-marker]

`migration.json` records where this migration fits in your migration history:

* the contract hash it starts `from`
* the contract hash it ends at, `to`
* when it was created
* its own hash, `migrationHash`, which `npx prisma migration check` reads

Do not edit `migration.json` or `ops.json` by hand, because both are written for you from `migration.ts`. Edit `migration.ts` and recompile instead.

Your [migration history](https://www.prisma.io/docs/orm/migrations/the-migration-graph) branches when two people plan migrations from the same contract state on separate branches, because each of those migrations records the same hash as the state it starts `from`. [A worked example](https://www.prisma.io/docs/orm/migrations/the-migration-graph#a-worked-example) shows the two migrations to plan after the merge, one per branch.

## Every operation checks itself [#every-operation-checks-itself]

Inside `ops.json`, an operation is not only the change itself: it also carries the checks that decide whether the change needs to run and whether it worked. Here are the parts of the operation that adds the `phone` column:

* **Precheck**: confirms the database is in the state the change expects, here that `"user"` has no `phone` column.
* **Execute**: the statements that make the change, here `ALTER TABLE "public"."user" ADD COLUMN "phone" text`.
* **Postcheck**: confirms the change is in the database, here that the `phone` column exists. Not every operation has a postcheck, and because the postcheck is what lets Prisma ORM tell that a change is already done, an operation without one is never skipped.

When `db migrate` reaches an operation, it runs the postcheck before the change as well as after, because if the postcheck already passes then the change is in the database and there is nothing to do, so the operation is skipped. If the postcheck does not pass, `db migrate` runs the precheck, then the statements, then the postcheck, and it stops the run if either check fails. Once the operations are done, it checks the database against the contract before it updates the marker, which catches a database that satisfied every individual operation but still doesn't match what you declared. If `"user"` already had a `phone` column of another type, for instance, the postcheck skips the operation and that last check fails the run.

Each operation also has an `operationClass`, which says what kind of change the operation makes to the database and which you choose yourself when you write a raw SQL operation:

* **Additive**: adds something new, like a column.
* **Widening**: loosens a constraint or lets a type accept more values, like dropping `NOT NULL` from a column.
* **Destructive**: removes or changes something that exists, like dropping a column, and can lose data.
* **Data**: changes rows, not structure.

Destructive operations are the only class that gets a warning, and it is only a warning: nothing stops and waits for your answer. `migration plan` prints `This migration contains destructive operations that may cause data loss.` when it writes the migration, and `npx prisma migration show <dir>` prints it for any migration on disk, so you see it while reviewing. `db migrate` runs those operations without asking and prints the warning again afterwards.

Not every `db migrate` run gets to the end, and what you do next depends on how it stopped.

A migration can fail while it runs, and what that leaves behind depends on your database. On PostgreSQL, one `db migrate` run is one transaction, so a failure leaves the database as it was before the run started. On MongoDB, a run is not one transaction, so the operations that finished before the failure stay in the database. [When something goes wrong](https://www.prisma.io/docs/orm/migrations/applying-a-migration#when-something-goes-wrong) covers recovery.

A run also ends early when the database isn't in the state your migration assumed, because a precheck then stops the run before the change is made, for example when a migration sets `NOT NULL` on a column that still holds `NULL`. The error names the operation and the check that failed, so you know exactly which assumption was wrong. Fill those rows in `migration.ts`, recompile, and run `db migrate` again, as [Recovery](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery#recovery-when-a-migration-fails-partway) shows.

## The commands [#the-commands]

The migration commands are part of the [Prisma ORM CLI](https://www.prisma.io/docs/cli) and run as `npx prisma <command>`. The commands in this table read only files, so they need no database connection:

| Command                                          | What it does                                                                                                                           |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| [`migration plan`](https://www.prisma.io/docs/cli/migration-plan)          | [Generate a migration](https://www.prisma.io/docs/orm/migrations/generating-a-migration) from your contract changes                                              |
| [`migration new`](https://www.prisma.io/docs/cli/migration-new)            | Write an empty migration for a [data-only or hand-written change](https://www.prisma.io/docs/orm/migrations/editing-a-migration#starting-from-a-blank-migration) |
| [`migration show <target>`](https://www.prisma.io/docs/cli/migration-show) | Print one migration's operations, DDL preview, and metadata                                                                            |
| `migration list`                                 | List every on-disk migration                                                                                                           |
| `migration graph`                                | Draw your [migration history](https://www.prisma.io/docs/orm/migrations/the-migration-graph) as a graph                                                          |
| `migration check`                                | Check that each migration's `migrationHash` still matches its files and no files are missing, for example in CI                        |

`<target>` is a migration's directory name, its path under `migrations/app/`, or the first 6 or more characters of its `migrationHash`.

The commands in this table connect to a database:

| Command                                     | What it does                                                                    |
| ------------------------------------------- | ------------------------------------------------------------------------------- |
| [`db migrate`](https://www.prisma.io/docs/cli/db-migrate)             | [Apply pending migrations](https://www.prisma.io/docs/orm/migrations/applying-a-migration)                |
| [`migration status`](https://www.prisma.io/docs/cli/migration-status) | Show which contract state the database matches and which migrations are pending |
| `migration log`                             | Show the history of migrations the database has actually applied                |

If you used Prisma ORM 7, these commands replace its migrate commands:

* `migrate dev` becomes `migration plan`, then `db migrate --advance-ref db`. To make a development database match the contract without migration files, it becomes `db update`, which changes the database directly and can point the `db` ref at the new state, as [The db ref](https://www.prisma.io/docs/orm/migrations/generating-a-migration#the-db-ref-skipping---from) lists.
* `migrate deploy` becomes `db migrate`.
* `migrate reset` has no equivalent. Drop and recreate the database with your own tools, then run `npx prisma db migrate --advance-ref db` to run your migrations again, or `npx prisma db init` to create what your contract declares without running them.
* `migrate diff` becomes `migration show <dir>`, which prints one migration, or `db update --dry-run`, which shows the operations that would make a database match the contract.
* Baselining, marking an existing database as already migrated, is now [`npx prisma db sign`](https://www.prisma.io/docs/cli/db-sign), which checks that the tables match your contract before it writes the marker. For a database Prisma ORM 7 migrated, follow [Prisma ORM 7 to 8 (PostgreSQL)](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql#4-transfer-migration-ownership).

[Coming from Prisma ORM 7](https://www.prisma.io/docs/orm/coming-from-prisma-orm-7#commands) has the full table, including `migrate resolve`.

Here is the whole loop, from idea to typed code:

<video preload="metadata" src="/docs/img/orm/next/migrations/migration-loop.mp4" aria-label="A 30-second walkthrough: a feature idea becomes a contract change, the migration is planned, reviewed, and applied, and the new column appears in Prisma Studio and in autocompleted application code." style="{ width: &#x22;100%&#x22;, borderRadius: &#x22;0.75rem&#x22;, border: &#x22;1px solid var(--color-fd-border)&#x22; }" />

## The same model for SQL and MongoDB [#the-same-model-for-sql-and-mongodb]

The commands, the file layout, the migration history, and the checks work the same way on PostgreSQL and MongoDB. On PostgreSQL, operations run SQL statements such as `ALTER TABLE`. On MongoDB, they create collections, indexes, and JSON Schema validators.

Each database keeps its own [marker and ledger](https://www.prisma.io/docs/orm/migrations/the-migration-graph#terms-used-on-this-page), where the ledger is that database's list of every migration applied to it, and `migration log` is the command that reads it.

> [!NOTE]
> Migrations are early
> 
> Planning, editing, applying, and [rolling back](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery) work today, but there is no squash command and no shadow-database dry run yet.

## Prompt your coding agent [#prompt-your-coding-agent]

Projects created with `npm create prisma@latest` include the [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8), instruction files for your coding agent. In an existing project, run `npx prisma skills sync`. Ask your agent to:

* "Add a `phone` field to the User model and plan a migration for it."
* "Show me the pending migrations and what SQL they will run."
* "Explain what the ops.json in the latest migration does."

## See also [#see-also]

* [Studio with Prisma ORM](https://www.prisma.io/docs/studio/prisma-next): read your applied migration history: a visual diff, the executed SQL, and a schema diff per migration
* [The migration graph](https://www.prisma.io/docs/orm/migrations/the-migration-graph): why migrations form a graph
* [Generating a migration](https://www.prisma.io/docs/orm/migrations/generating-a-migration) and [Applying a migration](https://www.prisma.io/docs/orm/migrations/applying-a-migration): the hands-on loop
* [Editing a migration](https://www.prisma.io/docs/orm/migrations/editing-a-migration): backfills, raw SQL, and the recompile step
* [Rethinking Database Migrations](https://www.prisma.io/blog/rethinking-database-migrations): why this design exists

## Related pages

- [`Applying a migration`](https://www.prisma.io/docs/orm/migrations/applying-a-migration): The db migrate command applies the migrations you planned, until your database matches your contract, with a preview, checks on every operation, and safe re-runs.
- [`Editing a migration`](https://www.prisma.io/docs/orm/migrations/editing-a-migration): A migration is TypeScript you own. Fill in backfills, reorder steps, or write raw SQL, then recompile it with one command.
- [`Generating a migration`](https://www.prisma.io/docs/orm/migrations/generating-a-migration): Turn a change to your contract into a migration you can review, with the migration plan command.
- [`Rollbacks and recovery`](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery): Rolling back is one more migration that makes the database match an earlier contract state. Recovery is fixing a failed migration and running it again.
- [`The migration graph`](https://www.prisma.io/docs/orm/migrations/the-migration-graph): You and a teammate each changed your Prisma contract on separate branches. The migration graph is how Prisma ORM applies both changes to every database after the branches merge.