| 1 | --- |
| 2 | title: Migrations |
| 3 | description: How Prisma.Contract and Prisma.Migrate run Prisma's contract emission, migration planning, and graph-ordered apply inside alchemy deploy. |
| 4 | --- |
| 5 | |
| 6 | Prisma ORM v8 replaces `prisma migrate dev`/`deploy` with a |
| 7 | contract-first migration system: `prisma migration plan` writes |
| 8 | **migration packages** and `prisma db migrate` applies them in graph |
| 9 | order. Alchemy runs both as resources in the deploy graph — |
| 10 | `Prisma.Contract` plans, `Prisma.Migrate` applies: |
| 11 | |
| 12 | ```typescript |
| 13 | import * as Prisma from "alchemy/Prisma"; |
| 14 | |
| 15 | const contract = yield* Prisma.Contract("contract"); |
| 16 | |
| 17 | yield* Prisma.Migrate("migrate", { |
| 18 | url: databaseUrl, |
| 19 | contract, |
| 20 | }); |
| 21 | ``` |
| 22 | |
| 23 | Here `databaseUrl` is a deploy-time database URL (plain or redacted), not the |
| 24 | runtime connection Effect passed to the query client. Register |
| 25 | `Prisma.providers()` alongside your database providers. |
| 26 | |
| 27 | Both [PSL-first and TypeScript-first contracts](/sql/prisma/contracts) use this |
| 28 | migration flow. Choose and configure your source in the Contracts guide before |
| 29 | planning changes. |
| 30 | |
| 31 | ## What a deploy actually does |
| 32 | |
| 33 | `Prisma.Contract` checks the emitted contract's storage hash against the |
| 34 | on-disk migration graph. Plan-time drift checks emit into a temporary directory, |
| 35 | without changing your workspace. When reconciliation is needed, it: |
| 36 | |
| 37 | 1. Runs `prisma contract emit` — regenerating `contract.json` |
| 38 | (runtime IR) and `contract.d.ts` (types) from your contract source. |
| 39 | 2. Compares the emitted contract's hash against the graph head. |
| 40 | 3. If the graph doesn't cover the contract, runs |
| 41 | `prisma migration plan` with an explicit `--from` at the |
| 42 | current head, writing a new package under `migrations/app/`. |
| 43 | |
| 44 | `Prisma.Migrate` then observes the database's **contract marker** (a |
| 45 | `prisma_contract.marker` row Prisma maintains) and applies only |
| 46 | the pending part of the graph — a fresh database is bootstrapped from |
| 47 | empty, an up-to-date one is a no-op, and re-running after a crash |
| 48 | converges. A deploy can plan and apply a new package in the same run; it does |
| 49 | not pause for review unless the plan needs a human decision. For reviewed |
| 50 | changes, [plan and commit locally](#generate-review-and-commit-before-application) |
| 51 | before deploying. |
| 52 | |
| 53 | ## Migration packages, not SQL files |
| 54 | |
| 55 | A planned migration is a directory, not a `.sql` file: |
| 56 | |
| 57 | ``` |
| 58 | migrations/ |
| 59 | ├── app/ |
| 60 | │ └── 20260812T1014_migration-03524e20/ |
| 61 | │ ├── migration.json # from/to contract hashes + package hash |
| 62 | │ ├── migration.ts # the operations, as reviewable TypeScript |
| 63 | │ └── ops.json # rendered operations (content-addressed) |
| 64 | └── snapshots/ |
| 65 | └── 03524e20.../ # content-addressed contract snapshots |
| 66 | ``` |
| 67 | |
| 68 | Commit the source contract, `migrations/` (including snapshots), emitted |
| 69 | `contract.json` and `contract.d.ts`, and any generated Effect modules used by |
| 70 | your application. Other environments consume these checked-in artifacts. |
| 71 | TypeScript-first applications can import the authored contract directly; the |
| 72 | deploy only plans a new package when the graph does not cover the contract. |
| 73 | |
| 74 | ## Placeholders need your decision |
| 75 | |
| 76 | Some changes cannot be planned mechanically — making a column required |
| 77 | needs a backfill for existing rows. Prisma renders those as |
| 78 | `placeholder(...)` closures in the package's `migration.ts`, and |
| 79 | alchemy **fails the deploy** rather than guessing (the SQL runs |
| 80 | against your real database later in the same deploy): |
| 81 | |
| 82 | 1. Edit `migrations/app/<dir>/migration.ts` and replace each |
| 83 | `placeholder(...)` with a typed query plan. |
| 84 | 2. Self-emit the package: `node migrations/app/<dir>/migration.ts`. |
| 85 | 3. Commit and re-deploy — the contract sees no drift and |
| 86 | `Prisma.Migrate` applies the finished package. |
| 87 | |
| 88 | Or abandon the change: delete the package directory and revert the |
| 89 | contract source. |
| 90 | |
| 91 | ## Generate, review, and commit before application |
| 92 | |
| 93 | Run the same CLI locally on each contract change so deployment consumes a |
| 94 | reviewed migration graph: |
| 95 | |
| 96 | ```sh |
| 97 | pnpm exec prisma contract emit |
| 98 | pnpm exec prisma migration plan --name add-posts |
| 99 | git add prisma.config.ts src/prisma migrations |
| 100 | git diff --cached |
| 101 | git commit -m "Add Prisma migration" |
| 102 | ``` |
| 103 | |
| 104 | Resolve any placeholders and self-emit their packages before staging. If the |
| 105 | application uses generated Effect outputs, run `pnpm exec alchemy prisma generate` |
| 106 | before staging too; native `prisma contract emit` does not generate them. Review |
| 107 | the source contract, operations, emitted artifacts, and snapshots together. |
| 108 | |
| 109 | The next deploy detects no drift when the committed graph covers the contract, |
| 110 | and `Prisma.Migrate` applies it. Alternatively, keep application in your CLI |
| 111 | workflow with `pnpm exec prisma db migrate --db "$DATABASE_URL"`. Add `--show` |
| 112 | to preview the pending migration path before application. |
| 113 | |
| 114 | ## The contract marker |
| 115 | |
| 116 | Prisma records the applied contract hash in the database itself |
| 117 | (`prisma_contract.marker`). `Prisma.Migrate`'s diff compares the |
| 118 | marker against the contract's hash, so databases changed out-of-band — |
| 119 | a marker reset, a branch recreated from another parent — are detected |
| 120 | and re-converged on the next deploy. The runtime client also verifies |
| 121 | the marker on first use by default, so an application deployed against a |
| 122 | stale database surfaces the drift instead of returning wrong answers. |
| 123 | |
| 124 | ## Destroy never unwinds the database |
| 125 | |
| 126 | Removing `Prisma.Contract` from the stack does not delete migration |
| 127 | packages or emitted artifacts (they are checked in), and destroying |
| 128 | `Prisma.Migrate` never drops tables or reverts migrations. Deleting the database |
| 129 | resource itself follows that provider's lifecycle. Rolling back a schema is |
| 130 | itself a migration: plan to an earlier contract with |
| 131 | `prisma migration plan --to <ref>`. |
| 132 | |
| 133 | ## Scope: Postgres 17+, RC caveats |
| 134 | |
| 135 | Prisma v8 currently ships one production-track target: **PostgreSQL |
| 136 | 17+** (`@prisma/orm-postgres`). There is no MySQL and no D1 in the v8 |
| 137 | line, so unlike [Drizzle](/sql/drizzle/migrations) there are no other |
| 138 | dialects to choose between — for those databases, use Drizzle or the |
| 139 | raw [Effect SQL clients](/sql/effect-sql/postgres). Alchemy pins the |
| 140 | exact RC version in its catalog; expect the RC line to keep moving |
| 141 | until 8.0.0 final. |
| 142 | |
| 143 | ## Where next |
| 144 | |
| 145 | - [Postgres](/sql/prisma/postgres) — the runtime query API. |
| 146 | - [Cloudflare integration](/cloudflare/data/prisma) — the full contract, database, and Worker setup. |
| 147 | - [SQL databases](/sql/databases) — database choices and canonical deployment guides. |
| 148 | - [Effect SQL: Migrations](/sql/effect-sql/migrations) — the plain |
| 149 | `.sql` migration contract used by Drizzle and the raw clients. |
| 150 | - [Prisma Postgres](/prisma) — provision the database with the Prisma |
| 151 | platform provider. |