Next.js & React

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.

Prisma 8 vs Prisma 7 cover: a Prisma 7 findMany query next to the Prisma 8 db.orm.public.User chained query, with latest = 8.0 RC, pin prisma@^7 and orm init chips
On this page
  1. What is Prisma 8?
  2. Is Prisma 8 stable? When is the release date?
  3. Why does npm install prisma install Prisma 8?
  4. What changed in Prisma 8?
  5. Commands
  6. Queries
  7. Schema types
  8. What is missing in Prisma 8?
  9. How do I migrate from Prisma 7 to Prisma 8?
  10. Step 1: Run orm init
  11. Step 2: Sign the database
  12. Step 3: Move one route
  13. Step 4: Hand migrations to Prisma 8
  14. What broke when I ran Prisma 7 and 8 side by side?
  15. Should I upgrade to Prisma 8 now?
  16. What happens to Prisma Accelerate?
  17. Prisma 8 upgrade checklist
  18. Common Prisma 8 mistakes
  19. Key takeaways

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:

Prisma ORM 7Prisma ORM 8
npm tagprisma@prev (7.10.1)prisma@latest (8.0.0-rc.23)
StatusStable, supportedRelease candidate, GA "expected in October 2026"
Runtime package@prisma/client + a driver adapter@prisma/orm-postgres (8.0.0-rc.18)
Schema fileschema.prismacontract.prisma (first line // use prisma-8)
Codegenprisma generateprisma contract emit
Queriesprisma.user.findMany({ where })db.orm.public.User.where(...).all()
Migrationsmigrate dev / migrate deploy (SQL)migration plan / db migrate (TypeScript)
DatabasesPostgreSQL, MySQL, SQL Server, SQLite, MongoDB, CockroachDBPostgreSQL; MongoDB early access; SQLite experimental
Node.js20.19+, 22.12+ or 24+22.18+, 24.11+ or 26+
TypeScript5.4+5.9+

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:

text
$ 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.

json
{
  "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#

Prisma 7Prisma 8
prisma generateprisma contract emit
prisma migrate devprisma db update, or prisma migration plan + prisma db migrate
prisma migrate deployprisma db migrate
prisma db pushprisma db init (empty database), then prisma db update
prisma db pullprisma contract infer
prisma formatprisma contract format
prisma migrate reset, prisma db seed, prisma studioNone in the Prisma 8 CLI

Queries#

Prisma 7Prisma 8
prisma.user.findMany({ where: { active: true } })db.orm.public.User.where({ active: true }).all()
findUnique / findFirst.where(...).first() (returns null)
select: { id: true }.select("id")
include: { posts: true }.include("posts")
orderBy: { id: "asc" }.orderBy((u) => u.id.asc())
take / skip.limit(n) / .offset(n)
create({ data: {...} }).create({...}) (no data wrapper)
contains: "ali" / mode: "insensitive".like("%ali%") / .ilike("%ali%")
$transaction(async (tx) => ...)db.transaction(async (tx) => ...) with tx.orm
$disconnect()db.close()

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.

Prisma 7 featureIn Prisma 8
$extendsWill not be added. Use middleware, Collection subclasses or scopes
{ increment: 1 }Not available. Use raw SQL or read and write in a transaction
JSON path filtersNot available. Jsonb compares as a whole; use raw SQL
connectOrCreate, nested update/upsert/delete, setNot available. Only nested create, connect, disconnect
isolationLevel, timeout, nested transactionsNot available
P2002 / P2025 error codesUse error.sqlState (23505 = unique violation) or codes like RUNTIME.NO_ROWS
omit, relationLoadStrategy, TypedSQL, read replicasNot available
has / hasSome on String[]Not available. The field works; filter with raw SQL
MySQL, SQL Server, CockroachDBNot yet. Stay on Prisma 7

I replaced an increment with the documented raw SQL builder, and it ran on 8.0.0-rc.18:

ts
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.

The five phases of a Prisma 7 to Prisma 8 migration: prepare Prisma 7 as prisma7, add Prisma 8, move routes one at a time, transfer migration ownership with db sign and migration plan, then remove Prisma 7

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#

bash
npx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma

Write 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:

text
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:

diff
   "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/:

ts
// 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#

text
$ 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 contract

db 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#

ts
// 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:

bash
npx prisma contract print --output prisma8/contract.prisma
# set contract: "prisma8/contract.prisma" in prisma.config.ts
npx prisma contract emit
npx prisma db sign

The printed contract produced the same hash as the Prisma 7 schema (unchanged, already signed), so nothing drifted. It mapped my types like this:

prisma
// 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:

text
$ 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 contract

The 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:

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:

text
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:

sql
SHOW timezone;  -- must be UTC (or Etc/UTC) while Prisma 7 and 8 both write

Also 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.

Decision flow for Prisma 8: stay on Prisma 7 for MySQL, SQL Server or CockroachDB, or if you use $extends, nested updates, increment, JSON filters or P2002 codes; otherwise trial Prisma 8 side by side or start new PostgreSQL projects on it

Your situationRecommendation
Prisma 6Upgrade to Prisma 7 before November 19, 2026
Prisma 7 on MySQL, SQL Server or CockroachDBStay on 7; Prisma 8 doesn't support them yet
Prisma 7 using $extends, nested updates, JSON filters or P2002Stay on 7 until those have replacements you accept
Prisma 7 on PostgreSQL, simple CRUDPin 7 now; trial 8 on one route after GA
New PostgreSQL projectPrisma 8 is reasonable; expect small API edits until GA
Using AccelerateSwitch connections before December 1, 2026 regardless

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.

  1. Pin prisma and @prisma/client to ^7 and replace every unpinned npx prisma in CI and Dockerfiles with npx prisma@7 or a lockfile install.
  2. Leave Accelerate before December 1, 2026, and move Early Access Prisma Postgres databases before November 1.
  3. Check requirements: Node.js 22.18+, 24.11+ or 26+, TypeScript 5.9+ with strict, PostgreSQL.
  4. Audit for missing features: $extends, increment, nested updates, JSON filters, isolation levels, P2002 handling.
  5. Check the database time zone with SHOW timezone; and set it to UTC if Prisma 7 and 8 will both write.
  6. Run npx prisma@latest orm init --from-prisma7-schema prisma/schema.prisma (add --confirm app in CI).
  7. Run npx prisma db sign, then move one low-risk route to db.orm.
  8. After each prisma7 migration, run prisma contract emit and prisma db sign.
  9. Transfer migrations with contract print, db sign, migration plan, db migrate --advance-ref db.
  10. 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.

MistakeSymptomFix
Unpinned prisma in CINo command registered for generatePin ^7 or use npx prisma@7
orm init in CI without consentCLI.CONSENT_REQUIREDAdd --confirm app
db init / db update on a Prisma 7 databaseSkips the signed handoff; the guide says to run neitherdb sign, then the phase 4 handoff
Not re-signing after prisma7 migratedb verify reports a contract mismatchcontract emit, then db sign
Catching P2002 in moved routesUnique violations crash as 500sCheck error.sqlState === "23505"
Server time zone not UTCPrisma 8 timestamps offset by hoursALTER DATABASE ... SET timezone TO 'UTC'
Expecting Date objectsStrings or Temporal.Instant insteadConvert at the API boundary
Hand-written contract on Node.js 24Temporal is undefinedimport "temporal-polyfill/full/global"

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