# Prisma ORM 7 to 8 (PostgreSQL) (/docs/guides/upgrade-prisma-orm/postgresql) > 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. Migrate a PostgreSQL project from Prisma ORM 7 to Prisma ORM 8 incrementally, with both versions running side by side Location: Guides > Upgrade Prisma ORM > Prisma ORM 7 to 8 (PostgreSQL) This guide is for teams running a Prisma ORM 7 application on PostgreSQL who want to move to **Prisma ORM 8** without a rewrite. You will install Prisma ORM 8 next to Prisma ORM 7 in the same application, move routes over one at a time, hand migration ownership to Prisma ORM 8, and remove Prisma ORM 7 once nothing depends on it. Both versions run against the same PostgreSQL database the whole time. The database, its data, and its connection string do not change; only application code and tooling do. Because each route stays on Prisma ORM 7 until you deliberately move it, the application remains shippable at every point in the migration. The guide covers **PostgreSQL only**. Guidance for other databases will follow. If you are coming from v6 on MongoDB, see the [MongoDB guide](https://www.prisma.io/docs/guides/upgrade-prisma-orm/mongodb). > [!NOTE] > Step 2.1 installs the `latest` version of both Prisma ORM 8 packages: `prisma`, the Prisma ORM 8 CLI, and `@prisma/orm-postgres`. This guide was written with `prisma` as of `8.0.0-rc.21` and `@prisma/orm-postgres` as of `8.0.0-rc.16`: the CLI is released separately, so the two numbers differ. Prisma ORM 7 stays at `7.10.0`: the example app in step 1.1 starts on it, and step 1.2 installs `@prisma/prisma7@7.10.0`. ## How the incremental migration works [#how-the-incremental-migration-works] The migration runs in five phases. The application works at the end of each one. 1. **Prepare Prisma ORM 7 for side-by-side operation.** Move Prisma ORM 7 onto its own package name, binary, and config file. No behavior changes. 2. **Add Prisma ORM 8.** Install the Prisma ORM 8 CLI and runtime with their own config, schema contract, and generated client. No application code uses them yet. 3. **Migrate one route.** One route runs on Prisma ORM 8 while the rest stay on Prisma ORM 7, all against the same database. 4. **Transfer migration ownership.** Prisma ORM 8 takes over planning and applying schema changes. 5. **Remove Prisma ORM 7** once nothing imports it. The ownership timeline matters more than the code timeline. Prisma ORM 7 owns schema migrations through phases 1 to 3, and routes move to Prisma ORM 8 independently of that. Prisma ORM 8 takes over migrations only in phase 4, after a database signature and a `db` ref are in place; the first `migration plan` then writes the baseline migration itself. You can pause between phases for as long as you need. The [prisma8-and-7-example](https://github.com/prisma/prisma8-and-7-example) repository shows the finished result of each phase (tags `step-0` through `step-3`). ## Prerequisites [#prerequisites] * **Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer**; Node.js 24 recommended * A working **Prisma ORM 7** application on PostgreSQL: `prisma.config.ts`, the `prisma-client` generator, and a driver adapter * **TypeScript 5.9 or newer** with `"strict": true`. [Step 2.6](#26-set-up-tsconfigjson-for-the-prisma-orm-8-client) tells you whether to change the `module` setting in `tsconfig.json` ## 1. Prepare Prisma ORM 7 for side-by-side operation [#1-prepare-prisma-orm-7-for-side-by-side-operation] Prisma ORM 8 expects the `prisma` package name, the `prisma` binary, and the `prisma.config.ts` file name. In this phase you move Prisma ORM 7 off those three names so Prisma ORM 8 can take them without ambiguity. Nothing migrates yet. ### 1.1. Confirm the application works [#11-confirm-the-application-works] The guide follows a small Hono API with two routes. Map the file names to your own project. The Prisma ORM 7 pieces that matter: ```json title="package.json (excerpt)" { "scripts": { "prisma:generate": "prisma generate", "db:migrate": "prisma migrate dev" }, "dependencies": { "@prisma/adapter-pg": "^7.10.0", "@prisma/client": "^7.10.0" }, "devDependencies": { "prisma": "^7.10.0" } } ``` ```prisma title="prisma/schema.prisma" generator client { provider = "prisma-client" output = "../generated/prisma" } datasource db { provider = "postgresql" } model User { id Int @id @default(autoincrement()) email String @unique name String? posts Post[] } model Post { id Int @id @default(autoincrement()) title String published Boolean @default(false) authorId Int author User @relation(fields: [authorId], references: [id], onDelete: Cascade) @@index([authorId]) } ``` ```typescript title="prisma.config.ts" import "dotenv/config"; import { defineConfig } from "prisma/config"; export default defineConfig({ schema: "prisma/schema.prisma", migrations: { path: "prisma/migrations", }, datasource: { url: process.env["DATABASE_URL"], }, }); ``` ```typescript title="src/db.ts" import "dotenv/config"; import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaClient } from "../generated/prisma/client"; const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! }); export const prisma = new PrismaClient({ adapter }); ``` Two routes read and write through this client, `/users` and `/posts`: ```typescript title="src/routes/users.ts" import { Hono } from "hono"; import { prisma } from "../db"; export const users = new Hono(); users.get("/", async (c) => { const result = await prisma.user.findMany({ include: { posts: true }, orderBy: { id: "asc" }, }); return c.json(result); }); users.post("/", async (c) => { const body = await c.req.json<{ email: string; name?: string }>(); const user = await prisma.user.create({ data: body }); return c.json(user, 201); }); ``` `src/routes/posts.ts` follows the same pattern for `Post`. Start the app and run a read and a write: #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` ```bash curl -X POST localhost:3000/users -H 'content-type: application/json' \ -d '{"email":"alice@prisma.io","name":"Alice"}' curl localhost:3000/users ``` Do not continue until both requests succeed. That confirms the Prisma ORM 7 application works before you change its configuration. ### 1.2. Replace the `prisma` package with `@prisma/prisma7` [#12-replace-the-prisma-package-with-prismaprisma7] #### bun ```bash bun remove prisma bun add --dev @prisma/prisma7@7.10.0 ``` #### pnpm ```bash pnpm remove prisma pnpm add --save-dev @prisma/prisma7@7.10.0 ``` #### yarn ```bash yarn remove prisma yarn add --dev @prisma/prisma7@7.10.0 ``` #### npm ```bash npm uninstall prisma npm install --save-dev @prisma/prisma7@7.10.0 ``` `@prisma/prisma7` is the same Prisma ORM 7 CLI under a version-specific name. It exposes a `prisma7` binary and keeps `prisma` 7 as a transitive dependency. Your `@prisma/client` and `@prisma/adapter-pg` dependencies stay untouched. ### 1.3. Rename the Prisma ORM 7 config [#13-rename-the-prisma-orm-7-config] ```bash mv prisma.config.ts prisma7.config.ts ``` ```typescript title="prisma7.config.ts" import "dotenv/config"; import { defineConfig } from "prisma/config"; // [!code --] import { defineConfig } from "@prisma/prisma7/config"; // [!code ++] export default defineConfig({ schema: "prisma/schema.prisma", migrations: { path: "prisma/migrations", }, datasource: { url: process.env["DATABASE_URL"], }, }); ``` The `prisma7` CLI discovers `prisma7.config.ts` automatically, so no `--config` flag is needed. Renaming frees the `prisma.config.ts` name for Prisma ORM 8, which only accepts its own config format under that name. ### 1.4. Point scripts at the `prisma7` binary [#14-point-scripts-at-the-prisma7-binary] ```json title="package.json (excerpt)" { "scripts": { "prisma:generate": "prisma generate", // [!code --] "db:migrate": "prisma migrate dev" // [!code --] "prisma7:generate": "prisma7 generate", // [!code ++] "prisma7:migrate": "prisma7 migrate dev" // [!code ++] } } ``` After Prisma ORM 8 is installed, the `prisma` binary runs the Prisma ORM 8 CLI. Update every script, CI job, and deployment command that must continue using Prisma ORM 7 to call `prisma7` instead. ### 1.5. Check that Prisma ORM 7 still works [#15-check-that-prisma-orm-7-still-works] #### bun ```bash bunx prisma7 generate bunx prisma7 migrate status ``` #### pnpm ```bash pnpm dlx prisma7 generate pnpm dlx prisma7 migrate status ``` #### yarn ```bash yarn dlx prisma7 generate yarn dlx prisma7 migrate status ``` #### npm ```bash npx prisma7 generate npx prisma7 migrate status ``` **Expected result:** `generate` writes the client to `generated/prisma` as before, and `migrate status` reports that the database schema is up to date. Start the app and query each route; behavior should be identical to step 1.1. ## 2. Add Prisma ORM 8 [#2-add-prisma-orm-8] ### 2.1. Install the Prisma ORM 8 packages [#21-install-the-prisma-orm-8-packages] #### bun ```bash bun add --dev prisma@latest bun add @prisma/orm-postgres ``` #### pnpm ```bash pnpm add --save-dev prisma@latest pnpm add @prisma/orm-postgres ``` #### yarn ```bash yarn add --dev prisma@latest yarn add @prisma/orm-postgres ``` #### npm ```bash npm install --save-dev prisma@latest npm install @prisma/orm-postgres ``` `prisma@latest` is the Prisma ORM 8 CLI, and its `prisma/config` subpath provides `definePrismaConfig` for the Prisma ORM 8 config file. Installing it locally (not only running it through `npx`) is what makes that import resolve. `@prisma/orm-postgres` is the PostgreSQL ORM runtime your application code will import. After this install, `npx prisma ` runs the Prisma ORM 8 CLI and `npx prisma7 ` runs Prisma ORM 7: #### bun ```bash bunx prisma --version ``` #### pnpm ```bash pnpm prisma --version ``` #### yarn ```bash yarn prisma --version ``` #### npm ```bash npx prisma --version ``` **Expected result:** the command prints `8.0.0-rc.22` or a newer version. That number does not match the version of `@prisma/orm-postgres`, because the CLI is released separately. ### 2.2. Create the Prisma ORM 8 config [#22-create-the-prisma-orm-8-config] ```typescript title="prisma.config.ts" import "dotenv/config"; import { definePrismaConfig } from "prisma/config"; import { defineConfig as definePostgresConfig } from "@prisma/orm-postgres/config"; export default definePrismaConfig({ orm: definePostgresConfig({ contract: "prisma8/contract.prisma", output: "generated/prisma8", db: { connection: process.env["DATABASE_URL"], }, }), }); ``` Both configs point at the **same** `DATABASE_URL`. Everything else is separate: | | Prisma ORM 7 | Prisma ORM 8 | | ---------------- | ---------------------- | ------------------------- | | CLI | `prisma7` | `prisma` | | Config | `prisma7.config.ts` | `prisma.config.ts` | | Schema | `prisma/schema.prisma` | `prisma8/contract.prisma` | | Generated client | `generated/prisma` | `generated/prisma8` | Because the config carries the connection, the Prisma ORM 8 CLI commands below don't need a `--db` flag. > [!NOTE] > Read the Prisma ORM 7 schema instead > > Instead of generating a contract file in steps 2.3 and 2.4, Prisma ORM 8 can read `prisma/schema.prisma` directly as its contract, so you keep one schema file while Prisma ORM 7 owns migrations. `schema.prisma` does not need the `// use prisma-8` first line that a contract file written for Prisma ORM 8 starts with (step 2.4 shows one). Skip steps 2.3 and 2.4, and change two lines in the `prisma.config.ts` you just wrote: > > ```typescript title="prisma.config.ts" > import "dotenv/config"; > import { definePrismaConfig } from "prisma/config"; > import { defineConfig as definePostgresConfig } from "@prisma/orm-postgres/config"; // [!code --] > import { defineConfig as definePostgresConfig, prisma7Schema } from "@prisma/orm-postgres/config"; // [!code ++] > > export default definePrismaConfig({ > orm: definePostgresConfig({ > contract: "prisma8/contract.prisma", // [!code --] > contract: prisma7Schema("prisma/schema.prisma"), // [!code ++] > output: "generated/prisma8", > db: { > connection: process.env["DATABASE_URL"], > }, > }), > }); > ``` > > Read this way, a `DateTime` field and the `@db.Timestamp`, `@db.Timestamptz`, `@db.Date`, and `@db.Time` fields are strings in Prisma ORM 8, such as `"2026-09-14 10:00:00.123"`, while Prisma ORM 7 keeps giving you `Date` objects. The Prisma ORM 8 client needs no `Temporal` for them. [Use a Prisma ORM 7 schema](https://www.prisma.io/docs/cli/configuration#use-a-prisma-orm-7-schema) has the details. > > After step 2.5, run `npx prisma db sign`. [`db sign`](https://www.prisma.io/docs/cli/db-sign) checks that the database matches the contract Prisma ORM 8 read from `schema.prisma` and records in the database that it does. Signing does not move your migrations to Prisma ORM 8. Prisma ORM 7 keeps planning and applying them, so after each Prisma ORM 7 migration, whether you ran `prisma7 migrate dev` or `prisma7 migrate deploy`, run `npx prisma contract emit` and then `npx prisma db sign` again. If you emit without signing, the Prisma ORM 8 client still runs queries, but `npx prisma db verify` reports that the contract no longer matches the one recorded in the database. [Use a Prisma ORM 7 schema](https://www.prisma.io/docs/cli/configuration#use-a-prisma-orm-7-schema) lists the parts of a Prisma ORM 7 schema that Prisma ORM 8 cannot read. > > If you have not started phase 1 yet, one command does phase 1 and this setup for you: > > > > > #### bun > ```bash title="Terminal" > bunx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma > ``` > > > #### pnpm > ```bash title="Terminal" > pnpm dlx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma > ``` > > > #### yarn > ```bash title="Terminal" > yarn dlx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma > ``` > > > #### npm > ```bash title="Terminal" > npx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma > ``` > > > > Write `prisma@latest`, because in a Prisma ORM 7 project `npx prisma` runs version 7. The command moves Prisma ORM 7 to the `prisma7` command and `prisma7.config.ts`, installs the Prisma ORM 8 packages, writes `prisma.config.ts` and a Prisma ORM 8 client in `src/prisma/db.ts`, and emits the contract. [`orm init` on a Prisma ORM 7 project](https://www.prisma.io/docs/cli/orm-init#on-a-prisma-orm-7-project) lists what it changes. Then run `npx prisma db sign` and continue with phase 3, using the `db` client from `src/prisma/db.ts` instead of adding one to `src/db.ts` in step 3.1. > > Prisma ORM 7 keeps owning migrations on this path until you are ready for [phase 4](#4-transfer-migration-ownership), where Prisma ORM 8 takes them over. Phase 4 needs a contract file written for Prisma ORM 8, so switch to one first. Either run `npx prisma contract print --output prisma8/contract.prisma`, which writes the contract Prisma ORM 8 read from `schema.prisma` as a Prisma ORM 8 contract file, or run steps 2.3 to 2.5. Then set `contract` in `prisma.config.ts` to `"prisma8/contract.prisma"`, the file you created. Then follow phase 4 as written. Its first step, `db sign`, checks the new contract against the database before Prisma ORM 8 takes over. ### 2.3. Infer the contract from the live database [#23-infer-the-contract-from-the-live-database] Prisma ORM 8 describes your schema as a [contract](https://www.prisma.io/docs/orm/contract-authoring/the-data-contract). Generate it from the database Prisma ORM 7 built: #### bun ```bash title="Terminal" bunx prisma contract infer --output prisma8/contract.prisma ``` #### pnpm ```bash title="Terminal" pnpm prisma contract infer --output prisma8/contract.prisma ``` #### yarn ```bash title="Terminal" yarn prisma contract infer --output prisma8/contract.prisma ``` #### npm ```bash title="Terminal" npx prisma contract infer --output prisma8/contract.prisma ``` ### 2.4. Edit the inferred contract [#24-edit-the-inferred-contract] `contract infer` reads every table in the database, so it also writes a `PrismaMigrations` model for `_prisma_migrations`, the table where Prisma ORM 7 records which migrations it has applied. Delete that model from the contract, because Prisma ORM 8 must not manage the table. The table itself stays in the database, and an extra table that the contract does not describe is fine. You do not need to add `@@map` to the other models: a model without `@@map` uses its name, exactly as written, as its table name, so `User` reads and writes the `"User"` table that Prisma ORM 7 created. After that edit, the contract looks like this: ```prisma title="prisma8/contract.prisma" // use prisma-8 model User { id Int @id(map: "User_pkey") @default(autoincrement()) email String name String? posts Post[] @@index([email], map: "User_email_key", unique: true) } model Post { id Int @id(map: "Post_pkey") @default(autoincrement()) title String published Boolean @default(false) authorId Int author User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade, map: "Post_authorId_fkey") @@index([authorId], map: "Post_authorId_idx") } ``` Keep `// use prisma-8` as the first line of `prisma8/contract.prisma`. `contract emit` reads only `.prisma` files that start with this line, and this is the only file in your contract, so without it `contract emit` fails with `CONTRACT.SOURCE_LOAD_FAILED`. A `schema.prisma` that Prisma ORM 8 reads through `prisma7Schema(...)`, as in the tip at the end of step 2.2, does not need the line. ### 2.5. Emit the contract artifacts [#25-emit-the-contract-artifacts] #### bun ```bash bunx prisma contract emit ``` #### pnpm ```bash pnpm prisma contract emit ``` #### yarn ```bash yarn prisma contract emit ``` #### npm ```bash npx prisma contract emit ``` `contract emit` writes `contract.json` and `contract.d.ts` to `generated/prisma8`, the runtime and type inputs for the Prisma ORM 8 client. Re-run it after every contract change. ### 2.6. Set up tsconfig.json for the Prisma ORM 8 client [#26-set-up-tsconfigjson-for-the-prisma-orm-8-client] Your `tsconfig.json` must allow the JSON import you will write in step 3.1: `import contractJson from "../generated/prisma8/contract.json" with { type: "json" }`. TypeScript accepts the `with { type: "json" }` part only when `module` is `esnext`, `node18`, `node20`, `nodenext`, or `preserve`, and only in a file it treats as an ES module. Any other value, such as `commonjs`, `es2022`, or `node16`, rejects it, and leaving `module` unset fails too. Take the first case below that matches how your app runs: * **`module` is already one of the five values and your build works with it, and either `package.json` has `"type": "module"` or `module` is `esnext` or `preserve`.** Keep it. Set `moduleResolution` to `bundler` if `module` is `esnext` or `preserve`, to `nodenext` if `module` is `nodenext`, and remove it if `module` is `node18` or `node20`. A `moduleResolution` of `node` or `node10` cannot resolve the `@prisma/orm-*` packages, so do not leave that in place. * **You compile with `tsc`, run the output with `node`, and `package.json` has no `"type": "module"` (or has `"type": "commonjs"`).** Your app runs as CommonJS, and it can stay that way. Set `"module": "nodenext"` and `"moduleResolution": "nodenext"`, and do not add `"type": "module"`. Prisma ORM 8 works from CommonJS code, and your relative imports stay as they are, without extensions. TypeScript treats `db.ts` as CommonJS here, so in step 3.1 write the JSON import without the `with` part and copy the other import lines as shown. ```typescript title="src/db.ts (CommonJS excerpt)" import contractJson from "../generated/prisma8/contract.json"; ``` * **The app runs through `tsx`, or a bundler such as `vite`, `next`, or `esbuild` builds it, and you never run `tsc` output with plain `node`.** Set `"module": "preserve"` and `"moduleResolution": "bundler"`. You do not need file extensions on relative imports, and the import lines in step 3.1 work as shown. The [example project](https://github.com/prisma/prisma8-and-7-example) is set up this way. If you run the `.ts` files directly with `node` instead, its type stripping needs the `.ts` extension on every relative import, so add `"allowImportingTsExtensions": true` as well. * **You compile with `tsc`, run the output with `node`, and `package.json` has `"type": "module"`, even if you use `tsx` in development.** Set `"module": "nodenext"` and `"moduleResolution": "nodenext"`. Node then requires an explicit file extension on every relative import, which for compiled TypeScript files is `.js`, so this is the one setup that makes you rewrite imports; [the TypeScript module reference](https://www.typescriptlang.org/docs/handbook/modules/reference.html#node16-node18-nodenext) explains the rule. Whichever case applies, add `"resolveJsonModule": true` so TypeScript can type the JSON import, and add `generated/prisma8/**/*.d.ts` to `include` so it sees the emitted `contract.d.ts`. If your `tsconfig.json` has no `include`, TypeScript already picks up every file under the project, so skip that part: ```json title="tsconfig.json (excerpt)" { "compilerOptions": { "resolveJsonModule": true // [!code ++] }, "include": [ "src/**/*.ts", "generated/prisma/**/*.ts", "generated/prisma8/**/*.d.ts" // [!code ++] ] } ``` **Check:** `npx tsc --noEmit` passes. Prisma ORM 8 is now installed and configured, but no application code uses it yet. This check does not test your `module` choice, because nothing imports `contract.json` until step 3.1; if TypeScript reports TS2823 there, the `module` value rejects the `with` part, and if it reports TS2856, the file compiles as CommonJS and the `with` part has to go, as the CommonJS case shows. ## 3. Migrate one route [#3-migrate-one-route] Pick one small route and move only that code. The rest of the application stays on Prisma ORM 7. ### 3.1. Instantiate both clients [#31-instantiate-both-clients] ```typescript title="src/db.ts" import "dotenv/config"; import { PrismaPg } from "@prisma/adapter-pg"; import postgres from "@prisma/orm-postgres/runtime"; // [!code ++] import type { Contract } from "../generated/prisma8/contract.d"; // [!code ++] import contractJson from "../generated/prisma8/contract.json" with { type: "json" }; // [!code ++] import { PrismaClient } from "../generated/prisma/client"; const connectionString = process.env.DATABASE_URL!; const adapter = new PrismaPg({ connectionString }); export const prisma = new PrismaClient({ adapter }); export const db = postgres({ url: connectionString, contractJson }); // [!code ++] ``` `prisma` is the Prisma ORM 7 client and `db` is the Prisma ORM 8 client, both connected to the same database. ### 3.2. Rewrite the route [#32-rewrite-the-route] Move the users route to the Prisma ORM 8 [ORM client](https://www.prisma.io/docs/orm/reference/orm-client). Queries start from `db.orm..` (`public` here) and chain instead of taking one options object: #### After ```typescript title="src/routes/users.ts" import { Hono } from "hono"; import { db } from "../db"; export const users = new Hono(); users.get("/", async (c) => { const result = await db.orm.public.User.include("posts", (posts) => posts.orderBy((post) => post.id.asc()), ) .orderBy((user) => user.id.asc()) .all(); return c.json(result); }); users.post("/", async (c) => { const body = await c.req.json<{ email: string; name?: string }>(); const user = await db.orm.public.User.create(body); return c.json(user, 201); }); ``` #### Before ```typescript title="src/routes/users.ts" import { Hono } from "hono"; import { prisma } from "../db"; export const users = new Hono(); users.get("/", async (c) => { const result = await prisma.user.findMany({ include: { posts: true }, orderBy: { id: "asc" }, }); return c.json(result); }); users.post("/", async (c) => { const body = await c.req.json<{ email: string; name?: string }>(); const user = await prisma.user.create({ data: body }); return c.json(user, 201); }); ``` `src/routes/posts.ts` stays unchanged, on Prisma ORM 7. ### 3.3. Exercise both code paths [#33-exercise-both-code-paths] Start the app and hit both routes: ```bash curl localhost:3000/users curl -X POST localhost:3000/posts -H 'content-type: application/json' \ -d '{"title":"Written by Prisma 7","authorId":1}' curl localhost:3000/users ``` **Expected result:** the first request runs through Prisma ORM 8. The second writes through Prisma ORM 7. The third, through Prisma ORM 8 again, includes the post Prisma ORM 7 just wrote. Remaining routes can move over the same way, one at a time, on any schedule. Prisma ORM 7 still owns schema migrations in this phase. If the schema changes, run `prisma7 migrate dev`, then re-run `contract infer` and `contract emit` so the Prisma ORM 8 contract stays current. ## 4. Transfer migration ownership [#4-transfer-migration-ownership] So far every schema change has gone through `prisma7 migrate dev`. In this phase Prisma ORM 8 takes over planning and applying schema changes, and `prisma/schema.prisma` is frozen. Treat the switch as a decision, not a routine step. After it, your team and your pipelines must stop using the Prisma ORM 7 migration workflow, even though routes still on the Prisma ORM 7 client keep working. See [how migrations work](https://www.prisma.io/docs/orm/migrations/how-migrations-work) for the full picture. Prisma ORM 8 tracks schema state with four pieces, and the handoff creates each one exactly once: * A **contract hash** identifies one version of the emitted contract. * A **migration** is an on-disk package recording how to get from one contract hash to another. `migrate` only replays recorded migrations; it never invents one. * The **marker** is Prisma ORM 8's record, stored in the database, of which contract hash the database currently satisfies. * A **ref** is a named pointer at a contract hash. `migration plan` uses the `db` ref as its starting point. Step 4.1 creates the marker and the ref with one command. The baseline migration is written by the first `migration plan` in step 4.3. ### 4.1. Sign the existing database [#41-sign-the-existing-database] Your database already has every table the contract describes, so adopt it rather than replay anything: #### bun ```bash bunx prisma db sign bunx prisma migration status ``` #### pnpm ```bash pnpm prisma db sign pnpm prisma migration status ``` #### yarn ```bash yarn prisma db sign yarn prisma migration status ``` #### npm ```bash npx prisma db sign npx prisma migration status ``` `db sign` verifies that the live schema matches the emitted contract, writes Prisma ORM 8's marker at that contract version, stores the contract snapshot under `migrations/snapshots/`, and points a [ref](https://www.prisma.io/docs/orm/migrations/the-migration-graph#name-important-states-with-refs) named `db` at it. `migration plan` starts from that ref, so plans chain from the schema Prisma ORM 7 built and contain only your own changes. **Expected result:** `Database signed`, and `migration status` shows the current and target contract hashes match with nothing pending. ### 4.2. Retire the Prisma ORM 7 migration scripts [#42-retire-the-prisma-orm-7-migration-scripts] Remove `prisma7 migrate` from your scripts so nobody runs it by accident. Keep `prisma7 generate`, because the legacy routes still need their client: ```json title="package.json (excerpt)" { "scripts": { "prisma7:generate": "prisma7 generate", "prisma7:migrate": "prisma7 migrate dev", // [!code --] "prisma8:migrate": "prisma db migrate --advance-ref db" // [!code ++] } } ``` ### 4.3. Verify the handoff with a schema change [#43-verify-the-handoff-with-a-schema-change] Verify the migration handoff with a small additive schema change. Add a field to the contract: ```prisma title="prisma8/contract.prisma (excerpt)" model User { id Int @id(map: "User_pkey") @default(autoincrement()) email String name String? bio String? // [!code ++] ... } ``` Emit, plan, and apply: #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add_user_bio bunx prisma db migrate --advance-ref db bunx prisma db verify ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add_user_bio pnpm prisma db migrate --advance-ref db pnpm prisma db verify ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_user_bio yarn prisma db migrate --advance-ref db yarn prisma db verify ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add_user_bio npx prisma db migrate --advance-ref db npx prisma db verify ``` **Expected result:** `migration plan` reports `Planned baseline + 1 operation(s)` and writes two packages under `migrations/app/`: a baseline that records the schema you adopted in step 4.1, and `add_user_bio` with the single operation `Add column "bio" to "User"`. `db migrate` skips the baseline, because the marker already records that state, and applies `add_user_bio`. `--advance-ref db` moves the ref so the next plan chains correctly, and `db verify` reports that marker and schema match the contract. Restart the app: the Prisma ORM 8 route returns users with `bio`, and the Prisma ORM 7 route keeps working untouched, because its client doesn't know about the new column. Additive changes like nullable columns are safe next to legacy Prisma ORM 7 code. Be careful with renames or drops of columns that Prisma ORM 7 routes still read. ## 5. Remove Prisma ORM 7 [#5-remove-prisma-orm-7] Migrate the remaining routes as in phase 3. For `posts` here, that means swapping `prisma.post.findMany(...)` for `db.orm.public.Post.include("author").all()` and `prisma.post.create({ data })` for `db.orm.public.Post.create(data)`. When nothing imports `generated/prisma` anymore, remove Prisma ORM 7: #### bun ```bash bun remove @prisma/prisma7 @prisma/client @prisma/adapter-pg ``` #### pnpm ```bash pnpm remove @prisma/prisma7 @prisma/client @prisma/adapter-pg ``` #### yarn ```bash yarn remove @prisma/prisma7 @prisma/client @prisma/adapter-pg ``` #### npm ```bash npm uninstall @prisma/prisma7 @prisma/client @prisma/adapter-pg ``` ```bash rm prisma7.config.ts rm -r prisma generated/prisma ``` Then delete the `prisma7:*` scripts from `package.json` and drop `generated/prisma/**/*.ts` from the `include` array in `tsconfig.json`. Verify the end state: #### bun ```bash bunx tsc --noEmit bunx prisma db verify ``` #### pnpm ```bash pnpm tsc --noEmit pnpm prisma db verify ``` #### yarn ```bash yarn tsc --noEmit yarn prisma db verify ``` #### npm ```bash npx tsc --noEmit npx prisma db verify ``` Start the app and run a query against every route. The application now runs entirely on Prisma ORM 8, with schema changes managed by `prisma migration plan` and `prisma db migrate`. > [!NOTE] > Prisma ORM 7's `_prisma_migrations` table remains in the database. It is inert (Prisma ORM 8 ignores it) and you can drop it whenever you like. ## Next steps [#next-steps] * [How migrations work in Prisma ORM](https://www.prisma.io/docs/orm/migrations/how-migrations-work): the day-to-day `contract emit` → `migration plan` → `migrate` loop for schema changes * [Contract authoring](https://www.prisma.io/docs/orm/contract-authoring/psl-syntax): the full PSL syntax for evolving `contract.prisma` * [Prisma ORM CLI reference](https://www.prisma.io/docs/cli): every command used in this guide ## Related pages - [`Prisma ORM 6 to 8 (MongoDB)`](https://www.prisma.io/docs/guides/upgrade-prisma-orm/mongodb): Migrate a MongoDB project from Prisma ORM 6 to Prisma ORM 8 - [`Upgrade to v1`](https://www.prisma.io/docs/guides/upgrade-prisma-orm/v1): Guide for upgrading from Prisma 1 to Prisma ORM - [`Upgrade to v3`](https://www.prisma.io/docs/guides/upgrade-prisma-orm/v3): Guide for upgrading to Prisma ORM v3 - [`Upgrade to v4`](https://www.prisma.io/docs/guides/upgrade-prisma-orm/v4): Guide for upgrading to Prisma ORM v4 - [`Upgrade to v5`](https://www.prisma.io/docs/guides/upgrade-prisma-orm/v5): Guide for upgrading to Prisma ORM v5