# Coming from Prisma ORM 7 (/docs/orm/coming-from-prisma-orm-7)

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

What each Prisma ORM 7 schema attribute, command, and query is called in Prisma ORM 8, and what is not available.

Location: ORM > Coming from Prisma ORM 7

If you know Prisma ORM 7, this page tells you what each thing you already use is called in Prisma ORM 8. Two renamed things to know before the tables: the schema file is now called the contract (the same file, renamed), and generating the client types is now called `emit`. This page is a lookup table, not a tutorial: one table each for the schema, the command-line tool, and the query API, and a final section on what is missing.

To move a running application, follow [Migrate from Prisma ORM 7 to 8](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql) instead. It runs both versions side by side against the same database and moves one part of the app at a time. To decide whether to move at all, see [Release status](https://www.prisma.io/docs/orm/release-status).

All examples use PostgreSQL.

## Before you start [#before-you-start]

If you are adding Prisma ORM 8 to an application that runs Prisma ORM 7, follow [Migrate from Prisma ORM 7 to 8](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql). It installs both versions side by side and takes over your existing database. Do not run the commands below in that project: `npm install prisma` installs version 8 and replaces the version 7 command-line tool.

In a new project, three commands set up Prisma ORM 8:

  

#### bun

```bash
bun add --dev prisma
bun add @prisma/orm-postgres
bunx prisma orm init --write-env
```

#### pnpm

```bash
pnpm add --save-dev prisma
pnpm add @prisma/orm-postgres
pnpm dlx prisma orm init --write-env
```

#### yarn

```bash
yarn add --dev prisma
yarn add @prisma/orm-postgres
yarn dlx prisma orm init --write-env
```

#### npm

```bash
npm install --save-dev prisma
npm install @prisma/orm-postgres
npx prisma orm init --write-env
```

`orm init` writes `prisma.config.ts`, a starter `src/prisma/contract.prisma`, `src/prisma/db.ts`, and `.env`. Put your connection string in `.env` as `DATABASE_URL`, write your models in the contract, and run `npx prisma contract emit`; `db.ts` imports the two files it writes.

Commands are written as `prisma ...` in the tables below. Run them as `npx prisma ...`.

## `schema.prisma` is now `contract.prisma` [#schema]

`schema.prisma` is now `src/prisma/contract.prisma`. It is the same file, renamed: you still describe your models in it. In Prisma ORM 8, "schema" means a PostgreSQL schema, the namespace your tables live in, usually `public`.

The biggest visible change is in field types. Where Prisma ORM 7 wrote a Prisma type plus a `@db.` attribute, such as `String @db.VarChar(255)`, Prisma ORM 8 writes the database type as the field type: `VarChar(255)`.

| Prisma ORM 7                                                                                      | Prisma ORM 8                                                                             | Notes                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schema.prisma`                                                                                   | `contract.prisma`                                                                        |                                                                                                                                                                                                                                                                                                                                                                           |
| (nothing)                                                                                         | `// use prisma-8` as the first line                                                      | `prisma orm init` writes it; add it yourself if you write the contract by hand. Without it the contract still works, but the [Prisma editor extension](https://www.prisma.io/docs/orm/contract-authoring/editor-support) does not recognise the file. Contracts from before `8.0.0-rc.10` say `// use prisma-next` instead; the editor extension still accepts that and rewrites it when you format |
| `generator client { ... }`                                                                        | removed                                                                                  | `emit` is the new word for `generate`. `prisma contract emit` writes two files next to your contract, `contract.json` and `contract.d.ts`. Commit both                                                                                                                                                                                                                    |
| `datasource db { ... }`                                                                           | the `orm` section of `prisma.config.ts`                                                  | the connection string and file paths live in the config file, not the schema. Example below                                                                                                                                                                                                                                                                               |
| `String @db.Text`                                                                                 | `String`                                                                                 | `String` is already stored as `text`. A known bug in one error message suggests `Text`; there is no such type                                                                                                                                                                                                                                                             |
| `String @db.VarChar(255)`                                                                         | `VarChar(255)`                                                                           | the same for every other `@db.` type: the attribute name becomes the field type. [PSL syntax](https://www.prisma.io/docs/orm/contract-authoring/psl-syntax#models-and-fields) lists them                                                                                                                                                                                                            |
| `Decimal @db.Decimal(10, 2)`                                                                      | `Numeric(10, 2)`                                                                         | `Decimal` on its own still exists and is a `numeric` column without a fixed precision                                                                                                                                                                                                                                                                                     |
| `Int`, `Boolean`, `Float`, `BigInt`, `Bytes`                                                      | unchanged                                                                                |                                                                                                                                                                                                                                                                                                                                                                           |
| `String @db.Uuid`                                                                                 | `Uuid`                                                                                   |                                                                                                                                                                                                                                                                                                                                                                           |
| `DateTime`, `DateTime @db.Timestamptz`                                                            | `DateTime`                                                                               | same name, and it is already a `timestamptz` column. Values come back as `Temporal.Instant`, not JavaScript `Date`; `new Date(value.epochMilliseconds)` converts one. See the note below the table                                                                                                                                                                        |
| `updatedAt DateTime @updatedAt`                                                                   | `updatedAt temporal.updatedAt()`                                                         | write `temporal.updatedAt()` where the type would go. It declares a `DateTime` field and sets it on every create and update. Any field name works. `temporal.` is part of the contract syntax; there is nothing to import                                                                                                                                                 |
| `createdAt DateTime @default(now())`                                                              | the same, or `createdAt temporal.createdAt()`                                            | both work                                                                                                                                                                                                                                                                                                                                                                 |
| `String @default(cuid())`                                                                         | `String @default(cuid(2))`                                                               | `2` is the cuid version. Plain `cuid()` is rejected                                                                                                                                                                                                                                                                                                                       |
| `Json`                                                                                            | `Jsonb`                                                                                  | Prisma ORM 7's `Json` stored a PostgreSQL `jsonb` column, so write `Jsonb`. In Prisma ORM 8, `Json` means PostgreSQL's older `json` type, which you almost certainly do not want                                                                                                                                                                                          |
| `enum Role { ADMIN USER }`, in a new database                                                     | `enum Role { ADMIN USER }`                                                               | same syntax. You can give members explicit values (`ADMIN = "admin"`). The column is stored as text, not as a PostgreSQL enum type                                                                                                                                                                                                                                        |
| `enum Role { ADMIN USER }` with a field `role Role`, in an existing Prisma ORM 7 database         | `native_enum Role { ADMIN = "ADMIN" USER = "USER" }` with the field `role pg.enum(Role)` | your database already has a PostgreSQL enum type for it; `native_enum` keeps it. Every member needs an explicit value. You can switch to a plain `enum` later; that changes the column type, so it needs a migration. `pg.` is part of the contract syntax, like `temporal.`                                                                                              |
| `String[]`                                                                                        | `String[]`                                                                               | unchanged on PostgreSQL. The list filters (`has`, `hasEvery`, `hasSome`, `isEmpty`) are not available; filter on a list with raw SQL for now (see below)                                                                                                                                                                                                                  |
| `@@schema("billing")`                                                                             | `namespace billing { model ... }`                                                        | a `namespace` block is a PostgreSQL schema; wrap the models in it. They become `db.orm.billing.Invoice` and so on                                                                                                                                                                                                                                                         |
| implicit many-to-many (`Post[]` on `Tag` and `Tag[]` on `Post`, with no model for the join table) | the same two list fields, plus a model for the join table                                | you write the join table as a model yourself. Example below                                                                                                                                                                                                                                                                                                               |
| `@id`, `@unique`, `@default`, `@relation`, `@map`, `@@map`, `@@index`, `@@id`, `@@unique`         | unchanged                                                                                |                                                                                                                                                                                                                                                                                                                                                                           |
| a required relation field over an optional foreign key, or the reverse                            | rejected                                                                                 | since `8.0.0-rc.10`, `contract emit` reports `PSL_RELATION_NULLABILITY_MISMATCH`. Give the relation field and its `@relation(fields: [...])` columns the same `?`, or none                                                                                                                                                                                                |

> [!NOTE]
> DateTime values and Node.js versions
> 
> The `DateTime` type reads and writes its values as `Temporal.Instant` objects. If you would rather work with the timestamp as a string, write `TimestamptzString` in place of `DateTime`.
> 
> `Temporal` is built into Node.js 26 and later, but not in Node.js 22 or 24. On those versions, install `temporal-polyfill` and add this line at the top of `src/prisma/db.ts`:
> 
> ```ts
> import "temporal-polyfill/full/global";
> ```
>
> Homebrew's build of Node.js 26 leaves `Temporal` out, so check with `node -p "typeof Temporal"`; if it prints `undefined`, add the polyfill.

A many-to-many relation keeps the two list fields and adds a model for the join table. Its primary key must be the two foreign keys and nothing else, as `@@id([postId, tagId])` below:

```prisma title="src/prisma/contract.prisma"
model Post {
  id   Int    @id @default(autoincrement())
  tags Tag[]
}

model Tag {
  id    Int    @id @default(autoincrement())
  posts Post[]
}

model PostTag {
  postId Int
  tagId  Int
  post   Post @relation(fields: [postId], references: [id])
  tag    Tag  @relation(fields: [tagId], references: [id])

  @@id([postId, tagId])
}
```

That is complete as written: you do not need a `PostTag[]` field on `Post` or `Tag`. `.include("tags")` gives you `post.tags` as a `Tag[]`, and `connect` and `disconnect` work on it. `set` does not; see [Not available](#not-available-yet) below.

If the join table already exists from Prisma ORM 7, it is named `_PostToTag` with columns `A` and `B`. Keep the table by mapping the model to those names: `@@map("_PostToTag")` on the model, `@map("A")` on the field that points at the model whose name sorts first alphabetically (`postId`), and `@map("B")` on the other (`tagId`).

The config file replaces the `generator` and `datasource` blocks. This one is for PostgreSQL:

```ts title="prisma.config.ts"
import "dotenv/config";
import { definePrismaConfig } from "prisma/config";
import { defineConfig as ormConfig } from "@prisma/orm-postgres/config";

export default definePrismaConfig({
  orm: ormConfig({
    contract: "./src/prisma/contract.prisma",
    db: {
      connection: process.env.DATABASE_URL!,
    },
  }),
});
```

The `orm` section takes these keys:

* `contract`: the path to your contract file.
* `db`: the connection.
* `output` (optional): where `contract.json` and `contract.d.ts` are written. Default: next to the contract.
* `extensions` (optional): database extensions, for example `extensions: [pgvector]` with `import pgvector from "@prisma/orm-extension-pgvector/control"`. See [Using extensions](https://www.prisma.io/docs/orm/extensions/using-extensions).
* `migrations` (optional): `{ dir: "migrations" }`, relative to `prisma.config.ts`. Your own migrations go in `migrations/app/` under it.

There is no `provider` setting; importing from `@prisma/orm-postgres/config` is what selects PostgreSQL.

## Commands [#commands]

The Prisma ORM 8 command-line tool groups commands by what they act on: `contract` for your contract file, `db` for the database you are connected to, and `migration` for the migration files in your repository.

| Prisma ORM 7                                                             | Prisma ORM 8                                                                                                                                           | Notes                                                                                                                                                                                                                                                                                                        |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `prisma generate`                                                        | `prisma contract emit`                                                                                                                                 | run it after every change to the contract. It writes `contract.json` and `contract.d.ts`; commit both                                                                                                                                                                                                        |
| `prisma migrate dev`                                                     | `prisma db update`, or `prisma migration plan` then `prisma db migrate`                                                                                | see the paragraph below the table                                                                                                                                                                                                                                                                            |
| `prisma migrate deploy`                                                  | `prisma db migrate`                                                                                                                                    | applies the migration files in your repository                                                                                                                                                                                                                                                               |
| `prisma db push`                                                         | `prisma db init` the first time, on an empty database; `prisma db update` after that                                                                   | `db init` creates the tables; `db update` changes existing tables to match the contract. For a database that already has your Prisma ORM 7 tables, run neither; see [step 4 of the migration guide](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql#4-transfer-migration-ownership). See [`db update`](https://www.prisma.io/docs/cli/db-update) |
| `prisma db pull`                                                         | `prisma contract infer`                                                                                                                                | writes a first draft of a contract from an existing database                                                                                                                                                                                                                                                 |
| `prisma migrate diff`                                                    | `prisma db update --dry-run` to see what would change in the database, or `prisma migration show <migration name>` to print what a migration file does | `<migration name>` is a folder name under `migrations/app/`, for example `20260911T1030_add_bio`. To diff two migrations into a new migration file, `prisma migration plan --from <migration name> --to <migration name>`                                                                                    |
| `prisma migrate resolve --applied`                                       | see [Rollbacks and recovery](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery)                                                                                   | for a migration that failed half way                                                                                                                                                                                                                                                                         |
| `prisma migrate resolve` for a database that already matches your schema | [step 4 of the migration guide](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql#4-transfer-migration-ownership)                                                  | the guide records which contract the database matches, both in the database and in your repository. Follow it rather than running the commands by hand                                                                                                                                                       |
| `prisma migrate reset`                                                   | none                                                                                                                                                   | drop and recreate the database with your database tools, then `prisma db init`                                                                                                                                                                                                                               |
| `prisma db seed`                                                         | none                                                                                                                                                   | there is no seed command. Write a script that imports `db` from `src/prisma/db.ts` and calls `.create(...)`, and run it the way you run any TypeScript file, for example `npx tsx src/prisma/seed.ts`                                                                                                        |
| `prisma studio`                                                          | none in the Prisma ORM 8 tool                                                                                                                          | run Studio from the Prisma ORM 7 tool, from a directory outside your project, with the connection string spelled out: `cd /tmp && npx prisma@7 studio --url "postgresql://user:password@localhost:5432/mydb"`. See [Studio with Prisma ORM](https://www.prisma.io/docs/studio/prisma-next)                                             |
| `prisma format`                                                          | `prisma contract format`                                                                                                                               |                                                                                                                                                                                                                                                                                                              |
| `prisma validate`                                                        | none                                                                                                                                                   | `prisma contract emit` fails if the contract is invalid, and `prisma db verify` checks it against the database                                                                                                                                                                                               |

Day to day, the loop that replaces `prisma migrate dev` is: edit the contract, run `prisma contract emit`, then `prisma db update` to apply the change to your development database. `db update --dry-run` shows what it would change, and `db update` asks before it drops anything. When you want a migration file to commit, run `prisma migration plan` instead of `db update`; it writes a folder under `migrations/app/` with a `migration.ts` you can read, edit, and commit. Then `prisma db migrate` applies it. See [Generating a migration](https://www.prisma.io/docs/orm/migrations/generating-a-migration).

Four new commands you will meet first:

* `prisma orm init` sets up Prisma ORM 8 in a project: config file, starter contract, and `db.ts` (see [Before you start](#before-you-start)).
* `prisma db init` creates the tables for your contract in an empty database. (`db update` is for a database that already has tables.)
* `prisma db verify` checks whether the database still matches your contract.
* `prisma db sign` records in the database that it matches your contract. Step 4 of the migration guide runs it for your existing database; you will rarely type it yourself.

The rest are in the [CLI reference](https://www.prisma.io/docs/cli).

## `src/prisma/db.ts` replaces `PrismaClient` [#srcprismadbts-replaces-prismaclient]

There is no generated `PrismaClient`: you create the client once, in `src/prisma/db.ts`, from the two files `prisma contract emit` wrote:

```ts title="src/prisma/db.ts"
import "dotenv/config";
import postgres from "@prisma/orm-postgres/runtime";
import type { Contract } from "./contract.d";
import contractJson from "./contract.json" with { type: "json" };

export const db = postgres<Contract>({
  contractJson,
  url: process.env.DATABASE_URL!,
});
```

`prisma orm init` writes this file for you. Every example on this page imports `db` from it.

`db` has these parts:

* `db.orm`: your models, by model name (`db.orm.public.User`).
* `db.sql`: your tables, by table name (`db.sql.public.user`; the table name is the model name with a lowercase first letter, unless you set `@@map`). You need it only for raw SQL.
* `db.raw.sql`: writes a raw SQL query.
* `db.runtime()`: the connection. It runs raw queries.
* `db.transaction`: runs several queries in one transaction.

To add middleware, add a `middleware: [...]` key to the `postgres({ ... })` call above. A middleware is a plain object with a name and one or more hook functions, for example `{ name: "log", async beforeQuery(plan) { console.log(plan.sql); } }`. [How middleware works](https://www.prisma.io/docs/orm/middleware/how-middleware-works) lists the hooks.

## Queries [#queries]

A query is a chain of calls that you `await` as a whole. The last call (`.all()`, `.first()`, `.create(...)`, and so on) says what you want back, instead of one method with one big options object.

| Prisma ORM 7                                          | Prisma ORM 8                                                                                                                                                                                                                                                                                                                  |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `new PrismaClient()`                                  | `db`, from `src/prisma/db.ts` above. The examples below assume they live in a file directly inside `src/`, so the import path is `./prisma/db`                                                                                                                                                                                |
| `prisma.user`                                         | `db.orm.public.User` (`public` is the PostgreSQL schema; unless you set one up, every table is in `public`)                                                                                                                                                                                                                   |
| `findMany({ where })`                                 | `.where(...).all()`                                                                                                                                                                                                                                                                                                           |
| `findFirst({ where })`                                | `.where(...).first()`. Returns `null` when nothing matches. `.first({ id: 1 })` is a shortcut for `.where({ id: 1 }).first()`                                                                                                                                                                                                 |
| `findUnique({ where })`                               | `.where(...).first()`, and check for `null`. There is no separate unique lookup                                                                                                                                                                                                                                               |
| `where: { id: 1, active: true }`                      | `.where({ id: 1, active: true })`; for comparisons, `.where((u) => u.age.gt(18))`. [Filter conditions and operators](https://www.prisma.io/docs/orm/reference/orm-client#filter-conditions-and-operators) lists them all                                                                                                                                |
| `select: { id: true, email: true }`                   | `.select("id", "email")`                                                                                                                                                                                                                                                                                                      |
| `include: { posts: { where: ... } }`                  | `.include("posts")`, or `.include("posts", (posts) => posts.where({ published: true }))` to filter or sort the related records                                                                                                                                                                                                |
| `where: { name: { contains: "ali" } }`                | `.where((u) => u.name.like("%ali%"))`; see [Not available](#not-available-yet) for `startsWith` and case-insensitive matching                                                                                                                                                                                                 |
| `orderBy: { createdAt: "desc" }`                      | `.orderBy((u) => u.createdAt.desc())`                                                                                                                                                                                                                                                                                         |
| `take` / `skip`                                       | `.limit(n)` / `.offset(n)`                                                                                                                                                                                                                                                                                                    |
| `distinct: ["country"]`                               | `.distinct("country")`                                                                                                                                                                                                                                                                                                        |
| `create({ data: { ... } })`                           | `.create({ ... })`, without the `data` wrapper                                                                                                                                                                                                                                                                                |
| `create({ data: { ..., posts: { create: [...] } } })` | `.create({ ..., posts: (p) => p.create([...]) })`; `connect` works the same way                                                                                                                                                                                                                                               |
| `createMany({ data: [...] })`                         | `.createAll([...])` to get the rows back, or `.createAndCount([...])` to get a count                                                                                                                                                                                                                                          |
| `update({ where, data })`                             | `.where(...).update({ ... })`                                                                                                                                                                                                                                                                                                 |
| `updateMany({ where, data })`                         | `.where(...).updateAll({ ... })` for the rows, or `.updateAndCount({ ... })` for a count                                                                                                                                                                                                                                      |
| `delete({ where })`                                   | `.where(...).delete()`                                                                                                                                                                                                                                                                                                        |
| `deleteMany({ where })`                               | `.where(...).deleteAll()` for the rows, or `.deleteAndCount()` for a count                                                                                                                                                                                                                                                    |
| `upsert({ where, create, update })`                   | `.upsert({ create: { email: "a@b.c", name: "A" }, update: { name: "A" }, conflictOn: { email: "a@b.c" } })`. `create` is the row to insert. `conflictOn` takes a unique column with its value; if a row with that value exists, `update` is applied to it instead of inserting. Leave `conflictOn` out to use the primary key |
| `count({ where })`                                    | `.where(...).aggregate((agg) => ({ total: agg.count() }))`, which returns one object, `{ total: 5 }`                                                                                                                                                                                                                          |
| `aggregate({ _sum, _avg })`                           | `.aggregate((agg) => ({ total: agg.sum("views"), average: agg.avg("views") }))`                                                                                                                                                                                                                                               |
| `groupBy({ by, _count })`                             | `.groupBy("userId").aggregate((agg) => ({ count: agg.count() }))`                                                                                                                                                                                                                                                             |
| `$transaction(async (tx) => ...)`                     | `db.transaction(async (tx) => ...)`; inside, query through `tx.orm` instead of `db.orm`                                                                                                                                                                                                                                       |
| `$queryRaw`                                           | see the raw SQL example below                                                                                                                                                                                                                                                                                                 |
| `$executeRaw`                                         | see the raw SQL example below                                                                                                                                                                                                                                                                                                 |
| `$connect()`                                          | `db.connect()`. You rarely need it: the client connects on the first query                                                                                                                                                                                                                                                    |
| `$disconnect()`                                       | `db.close()`                                                                                                                                                                                                                                                                                                                  |

### Find with a filter [#find-with-a-filter]

```ts title="Prisma ORM 7"
const users = await prisma.user.findMany({
  where: { active: true },
  select: { id: true, email: true },
  orderBy: { createdAt: "desc" },
  take: 10,
});
```

```ts title="Prisma ORM 8"
import { db } from "./prisma/db";

const users = await db.orm.public.User
  .where({ active: true })
  .select("id", "email")
  .orderBy((u) => u.createdAt.desc())
  .limit(10)
  .all();
```

### Create a record [#create-a-record]

```ts title="Prisma ORM 7"
const user = await prisma.user.create({
  data: { email: "alice@prisma.io", name: "Alice" },
});
```

```ts title="Prisma ORM 8"
const user = await db.orm.public.User.create({
  email: "alice@prisma.io",
  name: "Alice",
});
```

### Raw SQL [#raw-sql]

```ts title="Prisma ORM 7"
const rows = await prisma.$queryRaw`SELECT id, email FROM "User" WHERE active = true`;
await prisma.$executeRaw`UPDATE "User" SET active = false WHERE id = ${id}`;
```

```ts title="Prisma ORM 8"
// A query that returns rows must say what type each column is.
const user = db.sql.public.user;
const select = db.raw.sql`SELECT id, email FROM "user" WHERE active = true`
  .returnsRow({ id: user.columns.id, email: user.columns.email })
  .build();
const rows = await db.runtime().query(select);

// A statement that changes rows: ask for the count.
const update = db.raw.sql`UPDATE "user" SET active = false WHERE id = ${id}`
  .affectedCount()
  .build();
await db.runtime().execute(update);
```

The table is `user`, not `User`: Prisma ORM 8 names a table after its model with a lowercase first letter, unless the model sets `@@map`. Every raw query that returns rows needs `.returnsRow(...)` with a type per column, and you take the type from the table, as above. For a computed column, write the type name instead, such as `pg/int4@1` or `pg/text@1`; the `@1` is a version number and is always `1` today. [Raw queries](https://www.prisma.io/docs/orm/reference/raw-queries) lists the names. Finish the query with `.build()` and pass it to `db.runtime().query()` for rows or `db.runtime().execute()` for a count.

### Transaction [#transaction]

```ts title="Prisma ORM 7"
const [user, post] = await prisma.$transaction([
  prisma.user.create({ data: { email: "jane@prisma.io" } }),
  prisma.post.create({ data: { title: "Hello" } }),
]);
```

```ts title="Prisma ORM 8"
const result = await db.transaction(async (tx) => {
  const user = await tx.orm.public.User.create({ email: "jane@prisma.io" });
  const post = await tx.orm.public.Post.create({ title: "Hello", authorId: user.id });
  return { user, post };
});
```

## Not available [#not-available-yet]

Prisma ORM 7 features that have no direct form in Prisma ORM 8, with what to do instead. The status column says which: not available today, available in a different form, coming in the next release, or (for `$extends`) not coming.

| Prisma ORM 7 feature                                                | Status                        | What to do instead                                                                                                                                                                                                                                                                                                                                                                              |
| ------------------------------------------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `skipDuplicates` on `createMany`                                    | not available                 | `createMany` itself is `.createAll([...])` or `.createAndCount([...])` (see the queries table). To skip rows that already exist, `.upsert({ create, update: {}, conflictOn })` one row at a time; there is no batch form                                                                                                                                                                        |
| `{ increment: n }` / `{ decrement: n }` in an update                | not available                 | write the arithmetic as raw SQL (see the example above), or read the value and write it back inside `db.transaction(...)`                                                                                                                                                                                                                                                                       |
| `findUniqueOrThrow` / `findFirstOrThrow`                            | not available on the query    | the usual form is `.first()` and a `null` check. If you want the throw, write `await db.orm.public.User.where({ id }).all().firstOrThrow()`. It throws an error with code `RUNTIME.NO_ROWS` when nothing matches. Avoid it on a filter that can match many rows; it reads them all first                                                                                                        |
| `mode: "insensitive"`                                               | available in a different form | on PostgreSQL, `.ilike("%alice%")` on a text field. You write the `%` wildcards yourself                                                                                                                                                                                                                                                                                                        |
| `contains` / `startsWith` / `endsWith`                              | available in a different form | `.like("%alice%")`, `.like("alice%")`, `.like("%alice")`; you write the `%` wildcards yourself                                                                                                                                                                                                                                                                                                  |
| filtering inside JSON (`path`, `string_contains`, `array_contains`) | not available                 | a `Jsonb` field can only be compared as a whole (`eq`, `neq`, `in`, `notIn`) or checked for null. Write the query as raw SQL (see the example above)                                                                                                                                                                                                                                            |
| `$transaction([...])` with an array of queries                      | available in a different form | `db.transaction(async (tx) => ...)`; the queries inside run one after another                                                                                                                                                                                                                                                                                                                   |
| `Prisma.User` and `Prisma.UserGetPayload<...>`                      | available in a different form | `contract.d.ts` exports a `Models` namespace with one type per model. `Scalars<Models.public_User>` is the row a plain query returns, `Shape<Models.public_User, { posts: { "+": "id" \| "title" } }>` is a row with chosen relations, and `ResultType<typeof query>` is what a query you already wrote returns. See [Model and result types](https://www.prisma.io/docs/orm/reference/orm-client#model-and-result-types) |
| implicit many-to-many relations                                     | available in a different form | write the join table as a model; see the example under [Schema](#schema)                                                                                                                                                                                                                                                                                                                        |
| `@@map` on an `enum`                                                | not available                 | `contract emit` rejects it with `Unknown attribute "@@map" in "enum" block`. A plain `enum` is stored as text, so there is nothing to name. If you need a PostgreSQL enum type with a specific name, declare it with `native_enum`, which does accept `@@map`                                                                                                                                   |
| `$use` middleware                                                   | available in a different form | pass `middleware: [...]` when you create the client in `db.ts` (example in the `db.ts` section above). [How middleware works](https://www.prisma.io/docs/orm/middleware/how-middleware-works) lists the hooks                                                                                                                                                                                                             |
| `$extends`                                                          | replaced by middleware        | `$extends` will not be added. Middleware replaces it and can do more; see [How middleware works](https://www.prisma.io/docs/orm/middleware/how-middleware-works)                                                                                                                                                                                                                                                          |

Also not available today:

* Nested writes other than `create`, `connect`, and `disconnect`: `connectOrCreate`, nested `update`, `updateMany`, `upsert`, `delete`, `deleteMany`, and `set` on a relation. Change the related rows through their own model instead, for example `db.orm.public.Post.where({ authorId }).updateAll({ ... })`, inside `db.transaction(...)` if it must be atomic.
* The list filters `has`, `hasEvery`, `hasSome`, and `isEmpty` on `String[]` and other list fields. The fields themselves work; filter on them with raw SQL, for example ``db.raw.sql`SELECT id FROM post WHERE ${tag} = ANY(tags)` ``.
* Transaction options: `isolationLevel`, `timeout`, `maxWait`, and transactions inside transactions.
* `omit`, `relationLoadStrategy`, `Prisma.skip`, and the automatic batching of `findUnique` calls.
* The `P2002` / `P2025` style error codes. Errors carry a `code` such as `RUNTIME.NO_ROWS` instead, and a database error such as a unique-constraint violation carries the standard SQL state code in `error.sqlState` (`23505` for a unique violation, on every database), so catch it with `if (error instanceof Error && "sqlState" in error && error.sqlState === "23505")`. See the [error reference](https://www.prisma.io/docs/orm/reference/error-reference).
* `Prisma.sql`, `Prisma.join`, `Prisma.raw`, `Prisma.empty`, and TypedSQL. Use `db.raw.sql` (example above); [Raw queries](https://www.prisma.io/docs/orm/reference/raw-queries) covers building a query from pieces.
* Soft delete, validation rules in the schema, lifecycle hooks on models, and read replicas. For soft delete, add a nullable `deletedAt` field and filter on it. Validate in your application code. Use middleware for hooks. For read replicas, create one client per database.

## Where to go next [#where-to-go-next]

* [Migrate from Prisma ORM 7 to 8](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql), the step-by-step migration for a running PostgreSQL application.
* [Core concepts](https://www.prisma.io/docs/orm/core-concepts), for what a contract is and what `contract emit` does.
* [ORM client reference](https://www.prisma.io/docs/orm/reference/orm-client), for every query method.
* [Reading data](https://www.prisma.io/docs/orm/fundamentals/reading-data) and [Writing data](https://www.prisma.io/docs/orm/fundamentals/writing-data), for the query API in full.
* [Release status](https://www.prisma.io/docs/orm/release-status), for how close Prisma ORM 8 is to its final release, and how to stay on Prisma ORM 7.

## Related pages

- [`Core concepts`](https://www.prisma.io/docs/orm/core-concepts): The ideas every Prisma ORM command and API builds on: contracts, emitting, plans, the database signature, codecs, and the migration graph.
- [`Extensions`](https://www.prisma.io/docs/orm/extensions): Every package that plugs into Prisma ORM: database packages, column types, indexes, query operations, and middleware, by Prisma and the community.
- [`Overview`](https://www.prisma.io/docs/orm/data-modeling): Describe the data your application needs with models, primary keys, scalar fields, and relations.
- [`Prisma 7`](https://www.prisma.io/docs/orm/v7): Prisma ORM is a next-generation Node.js and TypeScript ORM that provides type-safe database access, migrations, and a visual data editor.
- [`Prisma ORM`](https://www.prisma.io/docs/orm/v6): Learn about Prisma ORM