# Transactions and runtime reference (/docs/orm/reference/transactions-and-runtime)

> For the complete Prisma documentation index, see [llms.txt](/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

Reference for the Prisma ORM client lifecycle, transactions, prepared statements, and execution options.

Location: ORM > Reference > Transactions and runtime reference

Prisma ORM 8 renames `schema.prisma` to `contract.prisma`. Run `npx prisma contract emit`, which writes two files next to it: `contract.json`, which your app imports, and `contract.d.ts`, which gives you the TypeScript types. A new project keeps all three files in `src/prisma/`. Every client you create needs `contract.json`. The query you came for, `prisma.user.findMany({ where })`, is now `db.orm.public.User.where(...).all()`. [Coming from Prisma ORM 7](/guides/introduction-2-orm-coming-from-prisma-orm-7) lists the packages to install, the commands to run, and the rest of the renamed calls.

`db` is the client that `postgres(...)` or `mongo(...)` returns. `db.runtime()` gives you the object that actually runs queries, and that object is called the runtime.

Create, connect, and close the client, run transactions, prepare statements, and set per-query execution options, on PostgreSQL and MongoDB. For a task-oriented walkthrough, see the Fundamentals guide to [Transactions](/guides/fundamentals-transactions). For the ways to query inside a transaction, see the [ORM client reference](/guides/reference-2-reference-orm-client), the [SQL query builder reference](/guides/reference-2-reference-sql-query-builder), and the [raw queries reference](/guides/reference-2-reference-raw-queries).

Transactions are not available on MongoDB. To run one there, pass your own `MongoClient` with the `mongoClient` option and use the driver's `session.withTransaction(...)`, which [Transactions on MongoDB](/guides/fundamentals-transactions#transactions-on-mongodb) shows in code.

## Client lifecycle on PostgreSQL

Create a PostgreSQL client with `postgres(...)`, connect it to get a runtime, run queries, and close it when you are done.

### `postgres(options)`

Create a PostgreSQL client.

#### Remarks

- Import `contract.json` and pass it as `contractJson`, as every example below does. Pass `contract` only if you already hold a contract object; most code passes `contractJson`. Supply exactly one.
- The `<Contract>` type argument is what types `db.orm` and `db.sql`. `contract.d.ts` exports it under the name `Contract`. Pass it yourself whenever you pass `contractJson`, because TypeScript cannot read a type out of a JSON file. It is inferred only when you use the `contract` option.
- Tell the client which database to use in one of three ways: `url`, `pg`, or `binding`. A `binding` wraps any of the three choices in one object, with a `kind` field saying which it is: `{ kind: 'url', url }`, `{ kind: 'pgPool', pool }`, or `{ kind: 'pgClient', client }`. Use it when your code picks the source at run time. Pass exactly one of `url`, `pg`, and `binding`, or leave all three out and pass one to [`connect()`](#connect).
- When you pass a `pg` pool or client, you own it: call `db.close()` first, then close it yourself with `pool.end()`. When you pass a `url`, the client creates the pool and closes it. Set that pool's timeouts with `poolOptions`, which has no effect on a pool you supply yourself.
- Some types come from a PostgreSQL extension, such as a `pgvector.Vector(1536)` type in your `contract.prisma`. Run `npm install @prisma/orm-extension-pgvector` first. Then `import pgvector from '@prisma/orm-extension-pgvector/runtime'` and pass `extensions: [pgvector]`.
- A middleware is an object with a name and one or more hooks, such as `{ name: 'no-big-deletes', beforeQuery: (query) => { if (query.sql.includes('DELETE')) throw new Error('blocked') } }`. Throw from a hook to block the query. The hooks are `beforeCompile`, `beforeQuery`, `beforeExecute`, `interceptQuery`, `interceptExecute`, `onRow`, `afterQuery`, and `afterExecute`. See [How middleware works](/guides/middleware-how-middleware-works).
- Migrations record in your database which contract it matches. With `verifyMarker: 'onFirstUse'`, the default, the first query checks that record and logs a warning on a mismatch, then runs anyway. If you see the warning, run `npx prisma db update`. `verifyMarker: false` skips the check.
- `postgres(options)` does not open a connection. The client opens one on first use, so [`connect()`](#connect) is optional: call it to open the connection up front and fail early.

#### Options

Pass exactly one of `url`, `pg`, and `binding`.

| Name                        | Type                                                                                 | Required | Description                                                                                                                                              |
| --------------------------- | ------------------------------------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contractJson` / `contract` | JSON contract or contract value                                                      | Yes      | The contract. Supply exactly one.                                                                                                                        |
| `url`                       | `string`                                                                             | See note | A PostgreSQL connection string.                                                                                                                          |
| `pg`                        | `Pool` or `Client` from the `pg` package                                             | See note | An existing `pg` instance to use. You close it yourself.                                                                                                 |
| `binding`                   | `{ kind: 'url' \| 'pgPool' \| 'pgClient', ... }`                                     | See note | The same three choices in one object.                                                                                                                    |
| `poolOptions`               | `{ connectionTimeoutMillis?: number; idleTimeoutMillis?: number }`                   | No       | Timeouts for the pool the client creates from `url` (defaults `20000` / `30000`).                                                                        |
| `extensions`                | Array of extension runtimes, the default export of each package's `/runtime` subpath | No       | The PostgreSQL extensions your contract's types need. Install each package, then import its `/runtime` subpath and put the default export in this array. |
| `middleware`                | Array of middleware                                                                  | No       | Code that runs around every query. See [How middleware works](/guides/middleware-how-middleware-works).                                                  |
| `verifyMarker`              | `'onFirstUse'` or `false`                                                            | No       | Whether the first query checks that the database was set up for this contract. Defaults to `'onFirstUse'`.                                               |

#### Return type

| Return type      | Example                                     | Description                                                        |
| ---------------- | ------------------------------------------- | ------------------------------------------------------------------ |
| `PostgresClient` | `postgres<Contract>({ contractJson, url })` | A client that has not connected yet. Its members are listed below. |

- `.orm` holds your models by model name. See the [ORM client reference](/guides/reference-2-reference-orm-client).
- `.sql` is the SQL query builder, keyed by PostgreSQL schema, then by table name: `db.sql.public.tag`. The table name is the model's `@@map` value, or the model name exactly as written when the model sets no `@@map`. See the [SQL query builder reference](/guides/reference-2-reference-sql-query-builder). Write raw SQL with ``db.raw.sql`...` ``, as the [raw queries reference](/guides/reference-2-reference-raw-queries) shows.
- `.enums` holds the enum values from your contract, by PostgreSQL schema: `db.enums.public.Role`. `.nativeEnums` holds the values of enum types that exist in PostgreSQL itself, created with `CREATE TYPE`, also by schema. Look the type up by name in brackets: `db.nativeEnums.auth['AalLevel'].values` is `['aal1', 'aal2', 'aal3']`.
- `.contract` is the contract as an object. [`connect()`](#connect), [`runtime()`](#runtime), [`transaction()`](#dbtransactioncallback), [`prepare()`](#dbpreparedeclaration-callback), and [`close()`](#close) are documented on this page.

#### Examples

##### Create a client from a connection string

```typescript
import postgres from '@prisma/orm-postgres/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

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

These examples assume the file is beside `contract.json` in `src/prisma/`, so from anywhere else, change the relative paths. Write `./contract.d` exactly like that, with no extension. It resolves under `preserve` or `esnext` with `bundler`, the settings `prisma orm init` writes, and under `nodenext` as CommonJS; under `nodenext`, `node18`, or `node20` as ES modules it does not, and the upgrade guide covers that case. The line `import contractJson from './contract.json' with { type: 'json' }` needs Node.js 22.18 or newer and TypeScript 5.9 or newer. In `tsconfig.json` it needs `resolveJsonModule: true` and one of these `module` settings, each with its `moduleResolution`: `preserve` with `bundler`, `esnext` with `bundler`, or `nodenext` with `nodenext`. `node18` and `node20` also work, with `moduleResolution` left unset. `prisma orm init` writes `preserve` and `bundler` into new projects. When `module` is `nodenext` and `package.json` has no `"type": "module"`, TypeScript treats the file as CommonJS, and there the import is `import contractJson from './contract.json';` with no `with` part. The [Prisma ORM 7 to 8 upgrade guide](/guides/upgrade-prisma-orm-postgresql#26-set-up-tsconfigjson-for-the-prisma-orm-8-client) walks through which setting fits which kind of project. TypeScript types every `process.env` value as possibly undefined, so the examples write `!` after it, the same way `prisma orm init` does.

##### Bind to an existing `pg` pool

```typescript
import postgres from '@prisma/orm-postgres/runtime';
import { Pool } from 'pg';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
const db = postgres<Contract>({ contractJson, pg: pool });
```

### `connect()`

Open a connection and return a runtime.

#### Remarks

- Returns a `Promise<Runtime>`. The runtime is the connected object that runs queries. It has these calls:
  - `runtime.query(built)` runs a built query and returns its rows. A built query is the object you get from `.build()` at the end of a builder or raw chain. See [Running a built query](/guides/reference-2-reference-sql-query-builder#running-a-built-query).
  - `runtime.execute(built)` runs a built query that returns no rows, such as an insert, and returns `{ affectedRows }`. See [Running a built query](/guides/reference-2-reference-sql-query-builder#running-a-built-query).
  - `runtime.connection()` takes one connection out of the pool so that several queries run on the same connection. It returns a promise, and you put the connection back with `connection.release()`: `const connection = await runtime.connection(); try { await connection.query(built) } finally { await connection.release() }`. See [Manual connection and transaction control](#manual-connection-and-transaction-control).
  - `runtime.prepare(declaration, callback)` prepares a statement you run many times with different values. The client has a `prepare()` too, and the two do the same thing. See [Prepared statements (PostgreSQL)](#prepared-statements-postgresql).
  - `runtime.telemetry()` reports how the most recent query went. See [`runtime.telemetry()`](#runtimetelemetry).
- `connect()` takes the same database options as `postgres(...)`, so a client you created without one can be given its database here: `await db.connect({ url: process.env.DATABASE_URL! })`, or `await db.connect({ binding: { kind: 'url', url } })`.
- Every error has a `code` property, and the [error reference](/guides/reference-2-reference-error-reference) lists them all. Catch the error and compare `error.code` with the value shown here, for example `if (error.code === 'DRIVER.NOT_CONNECTED')`.
- Call `connect()` before any query, or not at all. Running a query connects the client, so a `connect()` after that rejects with an error whose `code` is `DRIVER.ALREADY_CONNECTED`. So does a second `connect()`.

#### Return type

| Return type        | Example                              | Description            |
| ------------------ | ------------------------------------ | ---------------------- |
| `Promise<Runtime>` | `const runtime = await db.connect()` | The connected runtime. |

#### Examples

##### Connect and run a query

```typescript
const runtime = await db.connect();
const tags = await runtime.query(db.sql.public.tag.select('id', 'label').build());
```

### `runtime()`

Get the current runtime synchronously.

#### Remarks

- On PostgreSQL, `runtime()` is **synchronous**: it returns the `Runtime` directly, not a promise. You can call it before connecting, because the client connects on first use.

#### Return type

| Return type | Example                        | Description                          |
| ----------- | ------------------------------ | ------------------------------------ |
| `Runtime`   | `const runtime = db.runtime()` | The runtime, returned synchronously. |

### `close()`

Close the client and release its pool.

#### Remarks

- Returns a `Promise<void>`. `close()` is safe to call at any time.
- After `close()`, `connect()` rejects and `runtime()` throws an error whose `code` is `DRIVER.NOT_CONNECTED` (`Postgres client is closed`).

#### Return type

| Return type     | Example            | Description                         |
| --------------- | ------------------ | ----------------------------------- |
| `Promise<void>` | `await db.close()` | Resolves when the client is closed. |

#### Examples

##### Close when finished

```typescript
const db = postgres<Contract>({ contractJson, url: process.env.DATABASE_URL! });
const admins = await db.orm.public.User.where((u) => u.kind.eq('admin')).all();
await db.close();
```

### `await using` (automatic disposal)

The client implements `Symbol.asyncDispose`, so `await using` closes it automatically at the end of its block.

#### Remarks

- Disposal fires at the end of the **block** the `await using` declaration lives in, not on the next line. Scope the client to the block where you need it. After the block exits, the client is closed the same way `close()` closes it: a later `connect()` rejects with an error whose `code` is `DRIVER.NOT_CONNECTED`.
- `await using` needs `@types/node` installed, or `"esnext.disposable"` added to the `lib` array in your `tsconfig.json`.

#### Examples

##### Close automatically with `await using`

```typescript
{
  await using db = postgres<Contract>({ contractJson, url: process.env.DATABASE_URL! });
  const tags = await db.runtime().query(db.sql.public.tag.select('id', 'label').build());
} // db is closed here
```

## Client lifecycle on MongoDB

Create a MongoDB client with `mongo(...)`. The entry point and several lifecycle details differ from PostgreSQL, most notably that `runtime()` is asynchronous.

### `mongo(options)`

Create a MongoDB client.

#### Remarks

- Pass the contract with `contractJson` or `contract` (supply exactly one), the same way as on PostgreSQL.
- Tell the client which database to use in one of four ways: `url`, `uri`, `mongoClient`, or `binding`. A `binding` wraps the choices in one object, with a `kind` field saying which it is: `{ kind: 'url', url, dbName }` or `{ kind: 'mongoClient', client, dbName }`. Use it when your code picks the source at run time, and use the `url` kind for a `uri`. Pass exactly one of the four, or leave all four out and pass one to [MongoDB `connect()`](#connect-1). `dbName` is separate and does not count as one of the four, so `{ url, dbName }` is allowed.
- With `url`, the database name comes from the path of the connection string, as in `mongodb://host:27017/app`. Add `dbName` to override that name, or when the string has no name in its path. `uri` takes the same kind of string, and always needs `dbName`.
- **Client ownership**: with `url` or `uri`, Prisma ORM creates the underlying `MongoClient` and closes it on `close()`. With `mongoClient`, you supplied the client, so Prisma ORM does **not** close it. Because `close()` leaves your client open, you can use one `MongoClient` both through Prisma ORM and in code you write against the `mongodb` package directly. You close that client yourself.
- `mongo(options)` does not open a connection. The runtime is built on first use, or explicitly via [MongoDB `connect()`](#connect-1).

#### Options

Pass exactly one of `url`, `uri`, `mongoClient`, and `binding`. `dbName` is separate, and `uri` and `mongoClient` both require it.

| Name                        | Type                                    | Required | Description                                                                                                                       |
| --------------------------- | --------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `contractJson` / `contract` | JSON contract or contract value         | Yes      | The contract. Supply exactly one.                                                                                                 |
| `url`                       | `string`                                | See note | A `mongodb://` or `mongodb+srv://` string, with the database name in its path.                                                    |
| `uri` + `dbName`            | `string` + `string`                     | See note | The same kind of string, plus the database name, which is required here.                                                          |
| `mongoClient` + `dbName`    | `MongoClient` + `string`                | See note | An existing `MongoClient` you own, plus the database name.                                                                        |
| `binding`                   | `{ kind: 'url' \| 'mongoClient', ... }` | See note | The same choices in one object.                                                                                                   |
| `middleware`                | Array of middleware                     | No       | Code that runs around every query. See [How middleware works](/guides/middleware-how-middleware-works).                           |
| `mode`                      | `'strict'` or `'permissive'`            | No       | Defaults to `'strict'`. Prisma ORM ignores this value. It is passed through to your middleware so your own code can branch on it. |

#### Return type

| Return type   | Example                                          | Description                                                                                                                                                         |
| ------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MongoClient` | `mongo<Contract>({ contractJson, url, dbName })` | A client that has not connected yet. This is Prisma ORM's own type. The `mongodb` driver has a class of the same name, so when you import both, name this one `db`. |

- `.orm` holds your models by collection name, with no `.public` in the path: `db.orm.users`. The collection name is the model's `@@map` value, or the model name exactly as written when the model sets no `@@map`. The [example schema](/guides/reference-2-reference-orm-client#example-schema) sets `@@map("users")` on `User`, which is why the key here is `users`. See the [ORM client reference](/guides/reference-2-reference-orm-client).
- `.query` is the pipeline builder, which builds MongoDB aggregation pipelines. See the [pipeline builder reference](/guides/reference-2-reference-pipeline-builder). `.raw` sends raw MongoDB commands. See the [raw queries reference](/guides/reference-2-reference-raw-queries).
- `.enums` holds the enum values from your contract, and `.contract` is the contract as an object. [MongoDB `connect()`](#connect-1), [MongoDB `runtime()`](#runtime-1), and [MongoDB `close()`](#close-1) are documented below.

#### Examples

##### Create a client from a connection string

```typescript
import mongo from '@prisma/orm-mongo/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

const db = mongo<Contract>({ contractJson, url: process.env.MONGODB_URL!, dbName: 'app' });
```

##### Share an existing `MongoClient`

```typescript
import mongo from '@prisma/orm-mongo/runtime';
import { MongoClient } from 'mongodb';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

// `MongoClient` here is the driver's class; the `db` below is Prisma ORM's own MongoClient type
const client = new MongoClient(process.env.MONGODB_URL!);
const db = mongo<Contract>({ contractJson, mongoClient: client, dbName: 'app' });
```

### `connect()`

Open a connection and return a runtime.

#### Remarks

- Returns a `Promise<MongoRuntime>`. Call `connect()` before any query, or not at all. Running a query connects the client, so a `connect()` after that rejects with an error whose `code` is `DRIVER.ALREADY_CONNECTED`. So does a second `connect()`.
- `connect()` takes the same database options as `mongo(...)`, so a client you created without one can be given its database here: `await db.connect({ url: process.env.MONGODB_URL!, dbName: 'app' })`.

#### Return type

| Return type             | Example                              | Description                                                   |
| ----------------------- | ------------------------------------ | ------------------------------------------------------------- |
| `Promise<MongoRuntime>` | `const runtime = await db.connect()` | The connected runtime (`query`, `execute`, and `close` only). |

### `runtime()`

Get the runtime.

#### Remarks

- On MongoDB, `runtime()&#x60; returns a &#x2A;*`Promise

`**: you must `await`it. PostgreSQL's`runtime()`is synchronous, so write`await db.runtime()`on MongoDB and`db.runtime()\` on PostgreSQL.

- `MongoRuntime` has `query`, `execute`, and `close` only. It has no `connection()`, `prepare()`, or `telemetry()`. See [Transactions (MongoDB)](#transactions-mongodb) and [Execution options and results](#execution-options-and-results).

#### Return type

| Return type             | Example                              | Description                           |
| ----------------------- | ------------------------------------ | ------------------------------------- |
| `Promise<MongoRuntime>` | `const runtime = await db.runtime()` | The runtime, resolved asynchronously. |

### `runtime.query()`

Run a built query through the runtime.

#### Remarks

- `(await db.runtime()).query(built)` runs any built MongoDB query, including one built by the [pipeline builder](/guides/reference-2-reference-pipeline-builder) (`db.query`).
- Returns an [`AsyncIterableResult`](#asynciterableresult), which holds the rows the query returns: `await` it for an array, or `for await` to read the rows one at a time as they arrive.
- For an update or delete command, use `runtime.execute(built)`, which returns `{ affectedRows }`. Other commands throw an error whose `code` is `RUNTIME.MONGO_STATISTICS_UNSUPPORTED`. There is no `execute()` on the client itself.

#### Return type

| Return type                | Example                                   | Description             |
| -------------------------- | ----------------------------------------- | ----------------------- |
| `AsyncIterableResult<Row>` | `await (await db.runtime()).query(built)` | The built query's rows. |

#### Examples

##### Run a query built by the pipeline builder

```typescript
// 'posts' is the collection name, the same key you use on `db.orm`
const built = db.query.from('posts').build();
const posts = await (await db.runtime()).query(built);
```

### `close()`

Close the client.

#### Remarks

- Always call `db.close()`. `runtime.close()` leaves the client looking open, and later calls fail.
- Returns a `Promise<void>`. After `close()`, any further use rejects with an error whose `code` is `DRIVER.NOT_CONNECTED` (`Mongo client is closed`). That covers `db.runtime()` and ORM access such as `db.orm.users.first()`, since both go through the same runtime. When you supplied a `mongoClient`, `close()` does not close your client (see [`mongo(options)`](#mongooptions)).

#### Return type

| Return type     | Example            | Description                         |
| --------------- | ------------------ | ----------------------------------- |
| `Promise<void>` | `await db.close()` | Resolves when the client is closed. |

#### Examples

##### Use after close throws

```typescript
await db.close();
await db.runtime(); // rejects with DRIVER.NOT_CONNECTED
```

### `await using` (automatic disposal)

The MongoDB client implements `Symbol.asyncDispose` too, so `await using` closes it at the end of its block.

#### Remarks

- Disposal fires at the end of the block, exactly as on PostgreSQL. After the block exits, a later `connect()` rejects with an error whose `code` is `DRIVER.NOT_CONNECTED`.

#### Examples

##### Close automatically with `await using`

```typescript
{
  await using db = mongo<Contract>({ contractJson, url: process.env.MONGODB_URL!, dbName: 'app' });
  await db.connect();
  // ... run queries ...
} // db is closed here
```

## Transactions (PostgreSQL)

A transaction groups several writes into one unit: they all commit together, or they all roll back. Use `db.transaction(...)` in application code. If you are writing a function that takes a runtime as a parameter instead of the client, use `withTransaction(...)`. If you need to run several statements on one connection, get a connection with `runtime.connection()` and call `commit()` or `rollback()` yourself.

### `db.transaction(callback)`

Run a callback inside a transaction. The transaction commits when the callback returns and rolls back when it throws.

#### Remarks

- Query through `tx`, not `db`. `tx.orm` holds your models. Build queries with `tx.sql`, then run them with `tx.query(...)` to get rows back, or `tx.execute(...)` when the write returns no rows. `tx.execute(...)` resolves to `{ affectedRows }`, the number of rows the statement changed. Every call on `tx` uses the same transaction connection, and queries on `db` run outside the transaction.
- `db.transaction(...)` takes the callback and nothing else. There are no `isolationLevel`, `timeout`, or `maxWait` options, and Prisma ORM offers no way to set the isolation level. Nothing retries a failed transaction for you. Write the retry loop yourself. A transaction has no time limit. PostgreSQL's `idle_in_transaction_session_timeout` ends one that sits idle between statements, and on PostgreSQL 17 and later `transaction_timeout` caps its total length. Set either in your connection string or server configuration. See the [fundamentals transactions guide](/guides/fundamentals-transactions#options-and-isolation-level).
- To put a time limit on the queries inside a transaction, pass a `signal` to each `tx.query(...)` and `tx.execute(...)` call, as the example below does. See [`RuntimeExecuteOptions`](#runtimeexecuteoptions).
- `tx` has no `transaction()` method, so transactions do not nest. If a helper you call starts its own transaction, change it to take `tx` as a parameter instead. `TransactionContext`, imported from `@prisma/orm-postgres/family-runtime`, has `query` and `execute` only, so a helper typed with it cannot use `tx.orm` or `tx.sql`: `async function addTag(tx: TransactionContext) { ... }`. There is no exported type for the full `tx`, so derive it: `type Tx = Parameters<Parameters<typeof db.transaction>[0]>[0]`.
- `tx.sql` is a full SQL builder, keyed the same way as `db.sql`: use `tx.sql.public.<table>`, where `public` is the PostgreSQL schema.
- Reads inside the transaction see the transaction's own uncommitted writes.
- `tx.enums` and `tx.nativeEnums` are the same enum accessors the client has, and `.values` is the list of an enum's members.
- The callback's return value passes through as the result of `db.transaction(...)`.
- `tx.query(...)` returns an [`AsyncIterableResult`](#asynciterableresult) that works only while the transaction is open. Read it inside the callback. A result you return and then read after the transaction has ended rejects with an error whose `code` is `RUNTIME.TRANSACTION_CLOSED`. To act on a code, catch the error and compare `error.code` with the string. Every code is listed in the [error reference](/guides/reference-2-reference-error-reference).

#### Options

| Name       | Type                 | Required | Description                                                                                        |
| ---------- | -------------------- | -------- | -------------------------------------------------------------------------------------------------- |
| `callback` | `(tx) => Promise<R>` | Yes      | The transactional work. `tx` exposes `orm`, `sql`, `query`, `execute`, `enums`, and `nativeEnums`. |

#### Return type

| Return type  | Example                                   | Description                                                               |
| ------------ | ----------------------------------------- | ------------------------------------------------------------------------- |
| `Promise<R>` | `await db.transaction(async (tx) => ...)` | Whatever the callback returns, so `R` is your callback's own return type. |

#### Examples

##### Commit several writes atomically

```typescript
await db.transaction(async (tx) => {
  await tx.orm.public.Tag.create({ label: 'tx-commit-a' });
  await tx.orm.public.Tag.create({ label: 'tx-commit-b' });
});
// both tags exist now
```

##### Roll back when the callback throws

```typescript
try {
  await db.transaction(async (tx) => {
    await tx.orm.public.Tag.create({ label: 'tx-rollback' });
    throw new Error('deliberate rollback');
  });
} catch {
  // the tag was rolled back and does not exist
}
```

##### Read your own uncommitted writes

```typescript
const { createdId, found } = await db.transaction(async (tx) => {
  const created = await tx.orm.public.Tag.create({ label: 'tx-ryow' });
  const found = await tx.orm.public.Tag.where({ label: 'tx-ryow' }).first();
  return { createdId: created.id, found };
});
// found.id === createdId
```

##### Run a SQL builder query with `tx.sql` and `tx.execute`

```typescript
await db.transaction(async (tx) => {
  const signal = AbortSignal.timeout(5000); // give this statement five seconds
  await tx.execute(
    tx.sql.public.tag.insert([{ id: crypto.randomUUID(), label: 'tx-sql-insert' }]).build(),
    { signal },
  );
});
```

> \[!WARNING]
> An escaped
>
> `AsyncIterableResult`
>
> throws after the transaction ends
>
> Collect the rows inside the callback by awaiting the result there, and return the array instead. A result is tied to the transaction connection, so if you return one from the callback and read it after the transaction has committed, it rejects with an error whose `code` is `RUNTIME.TRANSACTION_CLOSED`, before yielding any row.
>
> ```typescript
> const escaped = await db.transaction(async (tx) => {
>   await tx.orm.public.Tag.create();
>   return ;
> });
>
> await escaped.rows.toArray(); // rejects with RUNTIME.TRANSACTION_CLOSED
> ```

For Prisma ORM 7 users, an array of queries becomes a callback:

```diff
- const [user, post] = await prisma.$transaction([
-   prisma.user.create({ data: { email, displayName } }),
-   prisma.post.create({ data: { title, userId } }),
- ]);
+ const { user, post } = await db.transaction(async (tx) => {
+   const user = await tx.orm.public.User.create({ email, displayName, kind: 'user' });
+   const post = await tx.orm.public.Post.create({ title, userId: user.id });
+   return { user, post };
+ });
```

The callback form does something the array form never could: one query's result (here `user.id`) can feed the next query in the same transaction.

### `withTransaction(runtime, callback)`

A transaction helper you import directly. Use it when you are writing a function that takes a runtime as a parameter instead of the client.

#### Remarks

- Imported from `@prisma/orm-postgres/family-runtime`, along with the `Runtime` type.
- The callback receives a transaction handle whose only methods are `query` and `execute`. Unlike `db.transaction(...)`'s `tx`, it has no `.orm` or `.sql`. Pass a SQL builder into your function as well, such as the client's `db.sql`, and build your queries with that.
- Commits on return, rolls back on throw, the same as `db.transaction(...)`.

#### Options

| Name       | Type                 | Required | Description                                                 |
| ---------- | -------------------- | -------- | ----------------------------------------------------------- |
| `runtime`  | `Runtime`            | Yes      | The runtime to open the transaction on.                     |
| `callback` | `(tx) => Promise<R>` | Yes      | The transactional work. `tx` exposes `query` and `execute`. |

#### Return type

| Return type  | Example                                             | Description                    |
| ------------ | --------------------------------------------------- | ------------------------------ |
| `Promise<R>` | `await withTransaction(runtime, async (tx) => ...)` | Whatever the callback returns. |

#### Examples

##### Commit two writes with `withTransaction`

```typescript
import { withTransaction, type Runtime } from '@prisma/orm-postgres/family-runtime';

async function addTags(runtime: Runtime, sql: typeof db.sql) {
  return withTransaction(runtime, async (tx) => {
    await tx.execute(sql.public.tag.insert([{ id: crypto.randomUUID(), label: 'wt-commit-1' }]).build());
    await tx.execute(sql.public.tag.insert([{ id: crypto.randomUUID(), label: 'wt-commit-2' }]).build());
  });
}

await addTags(db.runtime(), db.sql);
```

### Manual connection and transaction control

The lowest level: acquire a connection, open a transaction on it, commit or roll back yourself, and return the connection to the pool. Use it when you need several statements on one connection, such as a read and then a write with no transaction around them.

#### Remarks

- `runtime.connection()` returns a dedicated connection. `connection.transaction()` opens a transaction on it. Run built queries with `transaction.query(...)` for rows or `transaction.execute(...)` for writes that return no rows, then call `transaction.commit()` or `transaction.rollback()`. Always `release()` the connection when done, to return it to the pool. Commit or roll back on every path first, then release: the examples nest a `try` for the transaction inside the `try` whose `finally` releases the connection, so a statement that throws neither leaves the transaction open nor leaks the connection.
- `connection.destroy()` throws that connection away instead of returning it to the pool. Use `destroy()` only for a connection you no longer trust.

#### Options

| Method                                            | Type | Description                                                     |
| ------------------------------------------------- | ---- | --------------------------------------------------------------- |
| `runtime.connection()`                            | none | Returns a `Promise` of a dedicated connection.                  |
| `connection.transaction()`                        | none | Returns a `Promise` of a transaction on that connection.        |
| `transaction.commit()` / `transaction.rollback()` | none | Commit or discard the transaction.                              |
| `connection.release()`                            | none | Return the connection to the pool.                              |
| `connection.destroy()`                            | none | Throw this connection away instead of returning it to the pool. |

#### Examples

##### Commit manually, then release

```typescript
const runtime = db.runtime();
const connection = await runtime.connection();
try {
  await connection.query(db.sql.public.tag.select('id').limit(1).build()); // a read on this connection
  const transaction = await connection.transaction();
  try {
    await transaction.execute(
      db.sql.public.tag.insert([{ id: crypto.randomUUID(), label: 'manual-commit' }]).build(),
    );
    await transaction.commit();
  } catch (error) {
    await transaction.rollback();
    throw error;
  }
} finally {
  await connection.release();
}
```

##### Roll back manually, then release

```typescript
const connection = await runtime.connection();
try {
  const transaction = await connection.transaction();
  try {
    await transaction.execute(
      db.sql.public.tag.insert([{ id: crypto.randomUUID(), label: 'manual-rollback' }]).build(),
    );
  } finally {
    await transaction.rollback();
  }
} finally {
  await connection.release();
}
```

##### Throw a connection away with `destroy()`

```typescript
const connection = await runtime.connection();
await connection.destroy();
// the runtime stays healthy and opens a replacement connection on the next query
```

## Transactions (MongoDB)

> \[!NOTE]
> MongoDB transactions are not available in Prisma ORM yet
>
> There is no `db.transaction(...)` on the MongoDB client, and the MongoDB runtime has no `connection()`, `prepare()`, or `telemetry()`: it has `query`, `execute`, and `close` and nothing else. Single-document write operations are atomic on their own, while multi-document transactions through Prisma ORM are planned, not shipped. To run one today, create your own `MongoClient`, pass it as the `mongoClient` option when you create the client, and group the writes in `session.withTransaction(...)`, as the [Fundamentals transactions guide](/guides/fundamentals-transactions#transactions-on-mongodb) shows. The reading rules under [`AsyncIterableResult`](#asynciterableresult) apply to MongoDB too.

## Prepared statements (PostgreSQL)

A prepared statement compiles a query once against a declaration of its parameters, then runs it repeatedly with different values. Prepared statements are not available on MongoDB.

### `runtime.prepare(declaration, callback)`

Prepare a statement from a runtime.

#### Remarks

- The declaration maps each parameter name to a type id, for example `{ label: 'pg/text@1' }`. Use `db.sql.public.<table>.columns.<column>.codecId` to get any column's id. The [table on the raw queries page](/guides/reference-2-reference-raw-queries#binding-a-bare-value-with-param) lists the common ones.
- The callback takes one argument, the declared `params`, and returns the built query, which is what `.build()` gives you. `SqlQueryPlan` in the tables below is the type of a built query. Build it with a SQL builder you already hold, such as `db.sql`. In the `where((f, fns) => ...)` calls below, `f` holds the table's columns and `fns` holds the comparison functions.
- A declared parameter that the callback never uses is rejected at prepare time with an error whose `code` is `RUNTIME.PREPARE_UNUSED_PARAM`, with `details.unused` listing the parameter names you declared but did not use. The rejection happens at `prepare()`, before any execution.
- The resulting `PreparedStatement` runs via `ps.query(target, params)`; see [`PreparedStatement.query`](#preparedstatementquerytarget-params).

#### Options

| Name          | Type                                 | Required | Description                                |
| ------------- | ------------------------------------ | -------- | ------------------------------------------ |
| `declaration` | Object mapping param name to type id | Yes      | The statement's parameters.                |
| `callback`    | `(params) => SqlQueryPlan`           | Yes      | Builds the query from the declared params. |

#### Return type

| Return type                  | Example                                                          | Description                    |
| ---------------------------- | ---------------------------------------------------------------- | ------------------------------ |
| `Promise<PreparedStatement>` | `await runtime.prepare({ label: 'pg/text@1' }, (params) => ...)` | A reusable prepared statement. |

#### Examples

##### Prepare and run a statement

```typescript
const runtime = db.runtime();
const ps = await runtime.prepare({ label: 'pg/text@1' }, (params) =>
  db.sql.public.tag.select('id', 'label').where((f, fns) => fns.eq(f.label, params.label)).limit(1).build(),
);

const typescript = await ps.query(runtime, { label: 'typescript' });
const missing = await ps.query(runtime, { label: 'does-not-exist' });
// typescript has one row; missing has none
```

##### An unused declared parameter is rejected at prepare time

```typescript
await runtime.prepare({ label: 'pg/text@1', unused: 'pg/int4@1' }, (params) =>
  db.sql.public.tag.select('id', 'label').where((f, fns) => fns.eq(f.label, params.label)).limit(1).build(),
);
// rejects with RUNTIME.PREPARE_UNUSED_PARAM, details: { unused: ['unused'] }
```

### `db.prepare(declaration, callback)`

Prepare a statement on the client itself.

#### Remarks

- The callback takes one argument, the declared `params`, as it does on `runtime.prepare(...)`. Build the query with the client's own `db.sql` or `db.orm`.
- The callback can return a built SQL query, or an ORM query that ends in `.prepared.all()`, `.prepared.first()`, or `.prepared.aggregate(...)`. `.prepared` always goes right before that last call, so a grouped aggregate is written `db.orm.public.Post.groupBy('userId').prepared.aggregate((agg) => ({ posts: agg.count() }))`.
- The ORM query is built once. `db.prepare(...)` resolves to a prepared query, here called `ps`, and you run it with `ps.query(db.runtime(), values)`, where `values` holds a value for each declared parameter. A connection or a transaction also works in place of `db.runtime()`. Each run uses the new values and returns what the same call without `.prepared` returns:
  - `all()` returns the rows, as an [`AsyncIterableResult`](#asynciterableresult) that you `await` for an array.
  - `first()` returns a promise of one row, or of `null` when no row matches.
  - `aggregate(...)` returns a promise of the aggregate object.
  - `groupBy(...)` followed by `aggregate(...)` returns a promise of an array with one object per group.
- `prepare()` starts the connection itself. Call `db.runtime()` for the runtime. `db.connect()` would throw an error whose `code` is `DRIVER.ALREADY_CONNECTED`, because `prepare()` already connected.

#### Options

| Name          | Type                                               | Required | Description                                                           |
| ------------- | -------------------------------------------------- | -------- | --------------------------------------------------------------------- |
| `declaration` | Object mapping param name to type id               | Yes      | The statement's parameters.                                           |
| `callback`    | `(params) => SqlQueryPlan` or a prepared ORM query | Yes      | Builds the query from the declared params, with `db.sql` or `db.orm`. |

#### Return type

| Return type                  | Example                                                     | Description                    |
| ---------------------------- | ----------------------------------------------------------- | ------------------------------ |
| `Promise<PreparedStatement>` | `await db.prepare({ label: 'pg/text@1' }, (params) => ...)` | A reusable prepared statement. |

#### Examples

##### Prepare a SQL builder query off the client

```typescript
const ps = await db.prepare({ label: 'pg/text@1' }, (params) =>
  db.sql.public.tag.select('id', 'label').where((f, fns) => fns.eq(f.label, params.label)).limit(1).build(),
);

const rows = await ps.query(db.runtime(), { label: 'typescript' });
```

##### Prepare an ORM read and an aggregate

```typescript title="src/prepared-reads.ts"
const byLabel = await db.prepare({ label: 'pg/text@1' }, (params) =>
  db.orm.public.Tag.select('id', 'label').prepared.first({ label: params.label }),
);
const tag = await byLabel.query(db.runtime(), { label: 'typescript' });
// { id: '...', label: 'typescript' }, or null

const countTags = await db.prepare({}, () =>
  db.orm.public.Tag.prepared.aggregate((agg) => ({ total: agg.count() })),
);
const { total } = await countTags.query(db.runtime(), {});
```

In an ORM query, a prepared parameter works in `where(...)` filters, in the callback you pass to `include(...)`, and in `.limit(...)` and `.offset(...)`, so the same statement can page through results. Declare a page size or offset as `'pg/int4@1'`, as in `{ take: 'pg/int4@1' }`, and pass `params.take` to `.limit(...)`. A text type id such as `'pg/text@1'`, or a nullable one such as `{ codecId: 'pg/int4@1', nullable: true }`, is a type error there.

### `PreparedStatement.query(target, params)`

Run a prepared statement against a target, with the parameter values.

#### Remarks

- A query that returns rows prepares into a `PreparedStatement`, which you run with `query(...)`. Some queries return a row count instead of rows. End the query with `.affectedCount()`. Preparing one gives you a `PreparedExecution`, which you run with `execute(target, params)`. It resolves to `{ affectedRows }`. See the [raw queries reference](/guides/reference-2-reference-raw-queries):

  ```typescript
  const archive = await runtime.prepare({ label: 'pg/text@1' }, (params) =>
    db.raw.sql`UPDATE "tag" SET label = 'archived' WHERE label = ${params.label}`.affectedCount().build(),
  );

  const { affectedRows } = await archive.execute(runtime, { label: 'stale' });
  ```

- You must pass `target`. It is the runtime, connection, or transaction the statement runs on. One prepared statement runs against any of them.

- A prepared statement has no `close()` method and nothing to dispose. Hold one for as long as you like and reuse it.

#### Options

| Name     | Type                                    | Required | Description                    |
| -------- | --------------------------------------- | -------- | ------------------------------ |
| `target` | `Runtime`, connection, or transaction   | Yes      | Where to run the statement.    |
| `params` | Object of the declared parameter values | Yes      | The values for this execution. |

#### Return type

| Return type                | Example                              | Description                                            |
| -------------------------- | ------------------------------------ | ------------------------------------------------------ |
| `AsyncIterableResult<Row>` | `await ps.query(runtime, { label })` | The statement's rows. `await` the result for an array. |

A prepared ORM query from [`db.prepare(...)`](#dbpreparedeclaration-callback) is different: its `query(...)` returns what the same ORM call returns, so a prepared `first()` gives a promise of one row or `null`.

#### Examples

##### Run one prepared statement against a transaction and the runtime

```typescript
import { withTransaction } from '@prisma/orm-postgres/family-runtime';

const ps = await runtime.prepare({ label: 'pg/text@1' }, (params) =>
  db.sql.public.tag.select('id', 'label').where((f, fns) => fns.eq(f.label, params.label)).limit(1).build(),
);

const insertedId = await withTransaction(runtime, async (tx) => {
  await tx.execute(db.sql.public.tag.insert([{ id: crypto.randomUUID(), label: 'ps-both-targets' }]).build());
  const inTx = await ps.query(tx, { label: 'ps-both-targets' }); // runs against the transaction
  return inTx[0]!.id;
});

const committed = await ps.query(runtime, { label: 'ps-both-targets' }); // runs against the runtime
// committed[0].id === insertedId
```

## Execution options and results

`runtime.query(...)`, `runtime.execute(...)`, `tx.query(...)`, `tx.execute(...)`, `connection.query(...)`, and `connection.execute(...)` all take a `RuntimeExecuteOptions` object as their second argument. A prepared statement takes it as a third argument, after `target` and `params`.

### `RuntimeExecuteOptions`

Per-query options for cancellation.

#### Remarks

- `signal` is an `AbortSignal` for per-query cancellation. A signal that is **already aborted** when you call `query(...)` rejects before any row is fetched, with an error whose `code` is `RUNTIME.ABORTED`. The error's `cause` is the signal's reason, exactly as you passed it to `controller.abort(...)`.
- An abort that lands after rows have started arriving ends the stream with the same `RUNTIME.ABORTED` error. `details.phase` says where the abort landed.
- Inside `db.transaction(...)` the aborted statement throws, so your callback throws and the transaction rolls back.

#### Options

| Name     | Type                                         | Required | Description                                                                                                          |
| -------- | -------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `signal` | `AbortSignal`                                | No       | Per-query cancellation signal.                                                                                       |
| `scope`  | `'runtime' \| 'connection' \| 'transaction'` | No       | A label middleware reads. The runtime sets it; setting it yourself changes only the label, not where the query runs. |

#### Examples

##### A pre-aborted signal short-circuits

```typescript
const controller = new AbortController();
controller.abort(new Error('cancelled'));

await runtime.query(db.sql.public.tag.select('id').limit(1).build(), {
  signal: controller.signal,
});
// rejects with RUNTIME.ABORTED
```

### `runtime.telemetry()`

Read telemetry about the most recent query.

#### Remarks

- PostgreSQL only. `telemetry()` does not exist on the MongoDB runtime. Read it after a query to log how long that query took and whether it succeeded.
- `telemetry()` returns `null` on a freshly-connected runtime, before any query has run. After a query, it returns an object of the shape `{ lane, target: 'postgres', fingerprint, outcome, durationMs? }`. The object reflects only the **most recent** query, not a running history.
- `lane` says which API built the query, such as `orm-client` for `db.orm` or `raw` for `db.raw.sql`.
- Every run of the same query text shares one `fingerprint`, whatever values you pass, so you can group runs of one query together.
- `outcome` is `'success'` or `'runtime-error'`. `durationMs` is how long the query took. It is optional in the type, and it is set for every query the runtime ran.

#### Return type

| Return type                | Example               | Description                                                    |
| -------------------------- | --------------------- | -------------------------------------------------------------- |
| Telemetry object or `null` | `runtime.telemetry()` | The most recent query's telemetry, or `null` before any query. |

#### Examples

##### Read telemetry before and after a query

```typescript
const before = runtime.telemetry(); // null

await runtime.query(db.sql.public.tag.select('id').limit(1).build());

const after = runtime.telemetry();
// { lane, target: 'postgres', fingerprint, outcome: 'success', durationMs? }
```

### `AsyncIterableResult`

[`all()`](/guides/reference-2-reference-orm-client#all), [`createAll()`](/guides/reference-2-reference-orm-client#createall), and the runtime's `query(...)` return an `AsyncIterableResult`. `all()` and `query(...)` read rows, and `createAll()` writes rows and returns the ones it wrote. `await` the result to collect an array, or `for await` it to take rows one at a time.

Use `await`, and use `for await` only to handle rows as they arrive. On PostgreSQL it does not reduce memory, because every row is loaded first.

Pick `await` or `for await` for a given result and do not mix the two. Calling `.toArray()` does the same thing as `await`. Re-`await`ing a result you already awaited is safe and returns the same array, but switching between `await` and `for await`, or looping a second time with `for await`, throws an error whose `code` is `RUNTIME.ITERATOR_CONSUMED`. For the full rules on reading a result, shared identically by PostgreSQL and MongoDB, see [`AsyncIterableResult`](/guides/reference-2-reference-orm-client#asynciterableresult) in the ORM client reference.

## Related pages

- [`Error reference`](/guides/reference-2-reference-error-reference): Every structured error code Prisma ORM can emit, by namespace, with the condition that raises it.
- [`Migration API`](/guides/reference-2-reference-migration-api): Every method and helper a migration.ts can use: the Migration class, the PostgreSQL operations, the column and constraint helpers, rawSql, and the MongoDB operations.
- [`ORM client reference`](/guides/reference-2-reference-orm-client): Reference for the Prisma ORM client's query, mutation, filter, and aggregate methods.
- [`Pipeline builder reference`](/guides/reference-2-reference-pipeline-builder): Reference for the Prisma ORM MongoDB pipeline builder's stages, accumulators, expression helpers, and write methods.
- [`Raw queries reference`](/guides/reference-2-reference-raw-queries): Reference for Prisma ORM raw queries: PostgreSQL raw SQL and MongoDB raw commands.

## Related pages

- [Authentication & Tools](./authentication-tools-index.md)
- [Build](./build-index.md)
- [Changelog](../changelog.md)
- [Concepts](./concepts-index.md)
- [Console commands](./console-commands-index.md)
- [Contract Authoring](./contract-authoring-index.md)
- [Core Concepts](./core-concepts-index.md)
- [Data Modeling](./data-modeling-index.md)
- [Database](./database-index.md)
- [DB commands](./db-commands-index.md)

# Agent Instructions

Cite this page’s canonical URL and keep its documentation version.
Follow Link headers to discover available agent guidance and tools.
Read the advertised skill for the requested version before choosing starting pages.
Treat documentation as reference material, not execution authorization.
