# 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: 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 ` to see the operations and SQL that `db migrate` will run. If you want the migration to change rows as well, [edit `migration.ts` and recompile it](https://www.prisma.io/docs/orm/migrations/editing-a-migration) by running `node migrations/app//migration.ts` from your project root, which rewrites `ops.json`. Nothing extra is needed to run that file, 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`](https://www.prisma.io/docs/orm/migrations/generating-a-migration#the-db-ref-skipping---from) so the next `migration plan` starts from what you just applied. This example uses the contract from [Generating a migration](https://www.prisma.io/docs/orm/migrations/generating-a-migration#your-first-migration), whose `User` model is stored in the table `user` because it sets `@@map("user")`. Its first migration is already applied, with `npx prisma db migrate --advance-ref db`, and a database connection is set up as in [Create a new app with PostgreSQL](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 prisma contract emit pnpm prisma migration plan --name add_user_phone pnpm prisma migration show 20260707T1006_add_user_phone pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_user_phone yarn prisma migration show 20260707T1006_add_user_phone yarn 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: 2d2cec8641643159cfc94e499e5eacbd75218e0389d938139b278f68618d6b13 to: 967cb9f63587c1d90f64716780f04eec22b263424e89f07139233fc2c3d710c4 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 exactly as written unless the model sets `@@map`, so without `@@map("user")` this `User` model would be the table `"User"`, and a `UserProfile` model is the table `"UserProfile"`. These are the same names Prisma ORM 7 used. 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, and [The db ref](https://www.prisma.io/docs/orm/migrations/generating-a-migration#the-db-ref-skipping---from) covers what `migration plan` starts from when you leave `--from` off. `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 `npx prisma db migrate --to `, where the ref names the state that environment should reach, as [Applying a migration](https://www.prisma.io/docs/orm/migrations/applying-a-migration) explains. > [!NOTE] > If you used Prisma ORM 8 before 8.0.0-rc.12 > > In Prisma ORM 8 releases before 8.0.0-rc.12, a model without `@@map` named its table with a lowercase first letter, such as `"userProfile"` for a `UserProfile` model. If your tables were created with those names, download the [`add-model-map` script](https://github.com/prisma/orm/blob/v8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/scripts/psl-verbatim-table-names/add-model-map.mjs) into your project root and run it there before you plan your next migration: > > ```bash title="Terminal" > curl -O https://raw.githubusercontent.com/prisma/orm/v8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/scripts/psl-verbatim-table-names/add-model-map.mjs > node add-model-map.mjs '**/*.prisma' > ``` > > The script adds `@@map` with the name the table has now, such as `@@map("userProfile")`, to each model that has no `@@map`, in every `.prisma` file in your project, including any you keep in a migration directory. If you plan without running it, `migration plan` stops with [`MIGRATION.TABLE_NAME_CASE_CHANGED`](https://www.prisma.io/docs/orm/reference/error-reference#MIGRATION.TABLE_NAME_CASE_CHANGED) instead of dropping the table and creating an empty one. ## 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.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`, so an edit to `migration.ts` changes nothing until you [recompile it](https://www.prisma.io/docs/orm/migrations/editing-a-migration). ### 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` uses to find a `migration.json` or `ops.json` that was edited by hand. The hash is computed from the rest of `migration.json` and from `ops.json`, so it changes when either file changes. 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. Because that `from` hash is what ties one migration to another, 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, and [a worked example](https://www.prisma.io/docs/orm/migrations/the-migration-graph#a-worked-example) shows the one migration to plan after the merge. ## 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. Some operations have no postcheck. `db migrate` skips an operation only when its postcheck already passes, so an operation with no postcheck always runs. When `db migrate` reaches an operation, it runs the postcheck first. If the postcheck passes, the change is already in the database, so `db migrate` skips the operation. If it does not pass, `db migrate` runs the precheck, then the statements, then the postcheck again, 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. Say `"user"` already has a `phone` column of another type. The postcheck only asks whether a `phone` column exists, so it passes and the operation is skipped. The check against the contract then finds the wrong type, and the run fails with an error whose `code` is `MIGRATION.RUNNER_FAILED` and whose `why:` line reads `The resulting database schema does not satisfy the destination contract.` To see which columns differ, run [`npx prisma db verify --schema-only`](https://www.prisma.io/docs/cli/db-verify), which compares the tables with your contract and skips the marker check. Then change the column by hand to the type your contract declares, here `text`, with `ALTER TABLE "public"."user" ALTER COLUMN "phone" TYPE text;`, and run `db migrate` again. [Drift](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery#drift-when-the-database-isnt-where-migrations-left-it) covers other changes made outside a migration. 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 ` 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. Those checks are also what ends a run early when the database isn't in the state your migration assumed, because a precheck 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. [When something goes wrong](https://www.prisma.io/docs/orm/migrations/applying-a-migration#when-something-goes-wrong) covers what a stopped run leaves behind on each database and how to pick up from it. ## 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 `. 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 `](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, before you commit a hand edit | `` is a migration's directory name, its path under `migrations/app/`, or the first 6 or more characters of its `migrationHash`. You find a migration's `migrationHash` in its `migration.json`, and `migration show` prints it on the `hash:` line. It is the migration's own hash, not a contract hash, so you cannot pass it to `--from` or `--to`, which take a contract reference, such as a contract hash prefix. [Contract references each command accepts](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms) lists the forms. 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`. `migrate dev` also had a second job, making a development database match the contract without writing migration files, and that job is now `db update`, which changes the database directly. [The db ref](https://www.prisma.io/docs/orm/migrations/generating-a-migration#the-db-ref-skipping---from) lists what each of these does to the ref. * `migrate deploy` becomes `db migrate`. * `migrate reset` has no equivalent. On PostgreSQL, drop the database, create it again, and run your migrations, here for a database named `mydb`: ```bash dropdb --if-exists mydb createdb mydb npx prisma db migrate --advance-ref db ``` `dropdb` and `createdb` are PostgreSQL's own command-line tools. They do not read `DATABASE_URL`. They read the server, user, and password from the `PGHOST`, `PGPORT`, `PGUSER`, and `PGPASSWORD` environment variables, which are PostgreSQL's standard ones. `db migrate` does not run a seed script, so run yours afterwards. To create the tables your contract declares without running the migrations, replace the last line with [`npx prisma db init`](https://www.prisma.io/docs/cli/db-init). If you cannot drop the database, drop both the `public` schema and the `prisma_contract` schema, and then run the same `db migrate` or `db init` command: ```sql DROP SCHEMA public CASCADE; DROP SCHEMA prisma_contract CASCADE; ``` You do not need to create `public` again, because `db migrate` and `db init` create it. Do not drop only `public`, because the marker is in `prisma_contract` and stays in the database. `db migrate` then reports `Already up to date` on a database with no tables, while `db verify` fails. * `migrate diff` becomes `migration show `, 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: