Prisma 8 vs Prisma 7: Should You Upgrade? A Tested Migration Guide
Prisma 8 is a release candidate, yet npm install prisma already installs it. What changed from Prisma 7, what is missing, a tested side-by-side migration on PostgreSQL, two gotchas the docs skip, and whether to upgrade now.

On this page
Prisma 8 is a rewrite of Prisma ORM, not a version bump. The schema becomes a contract, prisma generate becomes prisma contract emit, @prisma/client is replaced by @prisma/orm-postgres, and queries are chained (db.orm.public.User.where(...).all()). It is still a release candidate, but since August 2026 npm install prisma installs it.
That last fact is the urgent part. Any Prisma 7 project, CI job or Dockerfile that installs prisma without a version now gets a CLI with no generate, migrate or db push. I upgraded a real Prisma 7 app on PostgreSQL 17 with the official side-by-side path, and this guide gives the commands, the output, two gotchas the docs don't spell out, and a clear answer on whether to move now. Facts are cited to the Prisma ORM release status page.
What is Prisma 8?#
Prisma 8 (Prisma ORM 8) is the next major version of Prisma ORM, a typed data layer for TypeScript. You describe models in a contract file, prisma contract emit writes contract.json and contract.d.ts, and you query through a db client from @prisma/orm-postgres. There is no generated PrismaClient, and migrations are TypeScript files you can review.
Key facts on October 17, 2026:
The CLI and the library are versioned separately, so prisma 8.0.0-rc.23 and @prisma/orm-postgres 8.0.0-rc.18 are a matching pair. Prisma says "any two latest versions work together."
Is Prisma 8 stable? When is the release date?#
No, not yet. Prisma 8 is a release candidate, and Prisma's release status page says general availability "is expected in October 2026." On October 17, 2026 the newest builds were prisma 8.0.0-rc.23 (published October 15) and @prisma/orm-postgres 8.0.0-rc.18 (October 14). Prisma warns that "some details of the API may still change."
Two support dates follow from the release:
- Prisma ORM 7 keeps receiving bug fixes and security updates "for 18 months from the day Prisma ORM 8 reaches general availability."
- Prisma ORM 6 receives security patches only until November 19, 2026.
So there is no rush to leave Prisma 7, but Prisma 6 projects should move to 7 now.
Why does npm install prisma install Prisma 8?#
Because Prisma moved the npm latest tag to Prisma 8 while it is still an RC. Prisma's August 28 changelog says the release "makes Prisma 8 available through prisma@latest." Prisma 7 is now prisma@prev. A Prisma 7 project that installs prisma without a version range gets a CLI that cannot read its workflow.
I ran the old commands with the Prisma 8 CLI in a Prisma 7 project:
$ npx prisma generate
✘ [CLI.UNKNOWN_COMMAND] No command registered for `generate`
$ npx prisma migrate deploy
✘ [CLI.UNKNOWN_COMMAND] No command registered for `migrate`, did you mean `migration`?
$ npx prisma db push
✘ [CLI.UNKNOWN_COMMAND] No command registered for `push`, did you mean `update`?Do this today, even if you never upgrade: pin both packages, then grep your CI, Dockerfiles and scripts for npx prisma and prisma@latest.
{
"devDependencies": { "prisma": "^7" },
"dependencies": { "@prisma/client": "^7" }
}Anywhere a command runs without a lockfile, write npx prisma@7 migrate deploy instead of npx prisma migrate deploy. If you build images with a multi-stage Dockerfile like the one in my Next.js Dockerfile guide, the npm ci step respects the lockfile; a separate npx prisma ... line in a later stage does not.
What changed in Prisma 8?#
Almost everything you type. The schema is a contract, codegen is contract emit, the client is built in src/prisma/db.ts, queries are chained, migrations are TypeScript packages tracked by a hash marker in the database, and several Prisma 7 features ($extends, nested updates, increment, JSON filters, P2002 codes) are missing. Your database tables and data do not change.
Commands#
Queries#
public is the PostgreSQL schema the table lives in. The full mapping is in Prisma's Coming from Prisma ORM 7 reference.
Schema types#
In a hand-written contract, @db. attributes become the field type (String @db.VarChar(255) is VarChar(255)), Json must be written Jsonb, @updatedAt is temporal.updatedAt(), cuid() is cuid(2), and implicit many-to-many relations need an explicit join model. DateTime values come back as Temporal.Instant, which Node.js 22 and 24 do not have built in. On my Node.js 24.22.0, node -p "typeof Temporal" printed undefined, so a hand-written contract needs temporal-polyfill.
What is missing in Prisma 8?#
Prisma 8 does not yet have several Prisma 7 features. The release status page lists $extends, "filtering inside JSON columns, atomic increment, most nested writes, transaction isolation levels, and the P2002-style error codes." $extends will not return; middleware, custom collections and scopes replace it. If your app relies on these, stay on Prisma 7 for now.
I replaced an increment with the documented raw SQL builder, and it ran on 8.0.0-rc.18:
import { db } from "./prisma/db.ts";
const post = db.sql.public.Post;
const bump = db.raw.sql`UPDATE "Post" SET views = views + 1 WHERE id = ${id} RETURNING id, views`
.returnsRow({ id: post.columns.id, views: post.columns.views })
.build();
const rows = await db.runtime().query(bump); // [ { id: 1, views: 3 } ]How do I migrate from Prisma 7 to Prisma 8?#
Run both versions side by side on the same database. One command (npx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma) renames Prisma 7 to prisma7, installs Prisma 8, and lets it read your existing schema.prisma. You then sign the database, move routes one at a time, and hand migrations to Prisma 8 only when you choose.

This is the path from Prisma's official Prisma ORM 7 to 8 guide, and the prisma8-and-7-example repo shows each phase. My test app was a Hono API on Prisma 7.10.1 with @prisma/adapter-pg, PostgreSQL 17.11, Node.js 24.22.0, a Role enum, a DateTime @default(now()), a Json? column and an increment update.
Step 1: Run orm init#
npx prisma@latest orm init --from-prisma7-schema prisma/schema.prismaWrite prisma@latest in full, because inside a Prisma 7 project npx prisma still runs version 7. In CI or from a coding agent, the command stops and asks for consent:
CLI.CONSENT_REQUIRED: "Prisma 7 is installed as `prisma`. Keep it as @prisma/prisma7
(binary prisma7) and move `prisma` to Prisma 8, and rename prisma.config.ts to
prisma7.config.ts?" requires explicit consent, and the session is not interactive.
Grant it by passing --confirm app.Note that it had already added @prisma/orm-postgres to package.json before it stopped. With --confirm app it finished and changed exactly this:
"dependencies": {
"@prisma/adapter-pg": "^7.10.1",
"@prisma/client": "^7.10.1",
+ "@prisma/orm-postgres": "^8.0.0-rc.18",
},
"devDependencies": {
+ "@prisma/cli-engine": "^0.7.0",
+ "@prisma/prisma7": "^7.10.1",
- "prisma": "^7.10.1",
+ "prisma": "^8.0.0-rc.23",
}It renamed prisma.config.ts to prisma7.config.ts (now importing @prisma/prisma7/config), wrote a new prisma.config.ts, and emitted the client into src/prisma/:
// prisma.config.ts (written by orm init)
import "dotenv/config";
import { definePrismaConfig } from "@prisma/cli-engine";
import { defineConfig as ormConfig, prisma7Schema } from "@prisma/orm-postgres/config";
export default definePrismaConfig({
orm: ormConfig({
contract: prisma7Schema("prisma/schema.prisma"),
output: "src/prisma",
db: { connection: process.env["DATABASE_URL"]! },
}),
});npx prisma --version now reports 8.0.0-rc.23, and npx prisma7 generate still writes the Prisma 7 client. Update every script and CI step that must keep using Prisma 7 to call prisma7.
Step 2: Sign the database#
$ npx prisma db sign
✔ app: signed b3b45c6c5fd8… (no marker before)
✔ Database signed
✔ Advanced ref "db" → b3b45c6c5fd8…
$ npx prisma db verify
✔ Database marker and schema match contractdb sign changes no tables. It adds a prisma_contract.marker table that records which contract hash the database satisfies. Prisma 7 still owns migrations, so after every prisma7 migrate dev or prisma7 migrate deploy, run npx prisma contract emit and npx prisma db sign again.
Step 3: Move one route#
// src/routes/users.ts — now on Prisma 8
import { db } from "../prisma/db.ts";
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 }>();
try {
return c.json(await db.orm.public.User.create(body), 201);
} catch (error) {
// No P2002 in Prisma 8: check the PostgreSQL SQL state instead
if (error instanceof Error && "sqlState" in error && error.sqlState === "23505") {
return c.json({ error: "email already exists" }, 409);
}
throw error;
}
});The posts route stayed on Prisma 7, including its increment. A post written through Prisma 7 appeared in the next Prisma 8 read, and a duplicate email returned my 409. tsc --noEmit passed on TypeScript 6.0.3 and, as a bonus, on TypeScript 7 7.0.2.
Step 4: Hand migrations to Prisma 8#
When you are ready, convert the Prisma 7 schema into a real contract, point the config at it, sign, then plan a change:
npx prisma contract print --output prisma8/contract.prisma
# set contract: "prisma8/contract.prisma" in prisma.config.ts
npx prisma contract emit
npx prisma db signThe printed contract produced the same hash as the Prisma 7 schema (unchanged, already signed), so nothing drifted. It mapped my types like this:
// use prisma-8
namespace public {
model User {
id Int @id @default(autoincrement())
email String
name String?
role pg.enum(Role) @default("USER")
createdAt TimestampString(3) @default(now())
posts Post[]
@@index([email], map: "User_email_key", unique: true)
}
native_enum Role {
USER = "USER"
ADMIN = "ADMIN"
}
}Then I added bio String? and ran the new migration loop:
$ npx prisma contract emit
$ npx prisma migration plan --name add_user_bio
→ Review migrations/app/20261017T1408_baseline and migrations/app/20261017T1409_add_user_bio
$ npx prisma db migrate --advance-ref db
✔ Applied 1 migration(s) (1 operation(s)) across 1 contract space(s)
├─ Add column "bio" to "User"
$ npx prisma db verify
✔ Database marker and schema match contractThe first plan writes a baseline (the schema Prisma 7 built) plus your change. db migrate skips the baseline because the marker already records it. Each migration is a reviewable migration.ts:
override get operations() {
return [
this.addColumn({
schema: "public",
table: "User",
column: col("bio", "text", { codecRef: { codecId: "pg/text@1" } }),
}),
];
}The Prisma 7 routes kept working after the new column. From here, retire prisma7 migrate from your scripts, move the remaining routes, and finally remove @prisma/prisma7, @prisma/client and @prisma/adapter-pg.
What broke when I ran Prisma 7 and 8 side by side?#
Two things the guide doesn't warn about. First, orm init needs --confirm app in any non-interactive shell (CI, Docker, coding agents). Second, createdAt timestamps written by Prisma 8 were 5 hours off. Prisma 7 fills @default(now()) in the client in UTC, but Prisma 8 leaves it to the database, which used my server's local time zone.
The evidence, from the same User table:
id | createdAt written by
1 | 2026-10-17 14:06:44.701 Prisma 7 (UTC)
2 | 2026-10-17 19:08:52.282 Prisma 8 (server time zone Asia/Karachi, UTC+5)
4 | 2026-10-17 14:09:08.795 Prisma 7 (UTC)Prisma 7 created the column as TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, which is timestamp without time zone. Prisma's own mapping table says @default(now()) in Prisma 8 "is a database default." For a timestamp column, PostgreSQL then writes the time in the session's TimeZone setting, which defaults to the server's. After ALTER DATABASE p8demo SET timezone TO 'UTC', Prisma 8 wrote 14:09:20 at 14:09:20 UTC.
Many managed PostgreSQL services default to UTC, so you may never see this. Check before mixing writers:
SHOW timezone; -- must be UTC (or Etc/UTC) while Prisma 7 and 8 both writeAlso note the data type change: read through prisma7Schema(...), DateTime comes back as a string such as "2026-10-17 14:06:44.701", not a Date. My JSON responses changed format between the two routes (2026-10-17T14:06:44.701Z from Prisma 7), so API clients that parse dates should be tested.
Should I upgrade to Prisma 8 now?#
Not for production apps yet. Pin Prisma 7 today, because that protects you from the latest tag change, and plan the move after GA. Start with Prisma 8 now only for new PostgreSQL projects that don't need the missing features, or to trial it on one route of an existing app with the side-by-side setup.

What happens to Prisma Accelerate?#
Prisma retires Accelerate on December 1, 2026, according to its October 2 changelog. If your connection string starts with prisma+postgres://accelerate.prisma-data.net, switch to a pooled connection or the serverless driver before then. Accelerate's query caching has no replacement, so remove cacheStrategy from your queries first.
The same changelog sets a second deadline: Prisma Postgres Early Access databases, the ones still on PostgreSQL 16, "are deleted on November 1, 2026 at 00:00 UTC." Move each one to a new database before then. Both deadlines apply whether you stay on Prisma 7 or move to 8.
Prisma 8 upgrade checklist#
Pin Prisma 7 first, then handle the deadlines, then migrate on your own schedule. These steps follow the official guide plus what I hit in testing.
- Pin
prismaand@prisma/clientto^7and replace every unpinnednpx prismain CI and Dockerfiles withnpx prisma@7or a lockfile install. - Leave Accelerate before December 1, 2026, and move Early Access Prisma Postgres databases before November 1.
- Check requirements: Node.js 22.18+, 24.11+ or 26+, TypeScript 5.9+ with
strict, PostgreSQL. - Audit for missing features:
$extends,increment, nested updates, JSON filters, isolation levels,P2002handling. - Check the database time zone with
SHOW timezone;and set it to UTC if Prisma 7 and 8 will both write. - Run
npx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma(add--confirm appin CI). - Run
npx prisma db sign, then move one low-risk route todb.orm. - After each
prisma7migration, runprisma contract emitandprisma db sign. - Transfer migrations with
contract print,db sign,migration plan,db migrate --advance-ref db. - Remove Prisma 7 when nothing imports
@prisma/client.
Common Prisma 8 mistakes#
The most common mistakes are letting prisma@latest slip into a Prisma 7 build, running Prisma 8's db init or db update against a database Prisma 7 already manages, forgetting to re-sign after a Prisma 7 migration, and assuming DateTime and error handling behave the same in both clients.
Related: I benchmarked Prisma 8 RC against Prisma 7 and Drizzle in Drizzle vs Prisma in 2026: Prisma 8 RC had the slowest bundled cold start (about 370 ms). If you use Better Auth, note that Better Auth 1.7 supports Prisma 5 to 7 but not Prisma 8 yet; see my Better Auth with Next.js 16 setup.
Key takeaways#
- Prisma 8 is a release candidate (prisma 8.0.0-rc.23, @prisma/orm-postgres 8.0.0-rc.18 on October 17, 2026) with GA expected in October 2026, but npm's latest tag already installs it.
- Pin prisma and @prisma/client to ^7 today; the Prisma 8 CLI has no generate, migrate or db push.
- Prisma 8 replaces schema.prisma with a contract, prisma generate with prisma contract emit, and @prisma/client with @prisma/orm-postgres and chained queries.
- $extends, increment, most nested writes, JSON filters, isolation levels and P2002 codes are missing, and only PostgreSQL is fully supported.
- orm init --from-prisma7-schema runs Prisma 7 and 8 side by side on one database; it needs --confirm app outside an interactive terminal.
- Check SHOW timezone before mixing writers: Prisma 8 leaves now() to the database, which skewed my timestamps by 5 hours.
- Prisma 7 is supported for 18 months after Prisma 8 GA, Prisma 6 security patches end November 19, 2026, and Accelerate retires December 1, 2026.
Questions
Share


