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

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

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](https://www.prisma.io/docs/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](https://www.prisma.io/docs/orm/fundamentals/transactions). For the ways to query inside a transaction, see the [ORM client reference](https://www.prisma.io/docs/orm/reference/orm-client), the [SQL query builder reference](https://www.prisma.io/docs/orm/reference/sql-query-builder), and the [raw queries reference](https://www.prisma.io/docs/orm/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](https://www.prisma.io/docs/orm/fundamentals/transactions#transactions-on-mongodb) shows in code.

## Client lifecycle on PostgreSQL [#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)` [#postgresoptions]

Create a PostgreSQL client.

#### Remarks [#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](https://www.prisma.io/docs/orm/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 [#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](https://www.prisma.io/docs/orm/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]

| 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](https://www.prisma.io/docs/orm/reference/orm-client).
* `.sql` is the SQL query builder, keyed by schema, then table name: `db.sql.public.tag`. The table name is the model's `@@map` value, or the model name with a lowercase first letter when the model sets no `@@map`. See the [SQL query builder reference](https://www.prisma.io/docs/orm/reference/sql-query-builder). `.raw` writes raw SQL as a template string. See the [raw queries reference](https://www.prisma.io/docs/orm/reference/raw-queries).
* `.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 [#examples]

##### Create a client from a connection string [#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. The line `import contractJson from './contract.json' with { type: 'json' }`, which `prisma orm init` writes, needs Node.js 22.18 or newer, and these `tsconfig.json` settings: `resolveJsonModule: true`, `module: "preserve"`, and `moduleResolution: "bundler"`. 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 [#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()` [#connect]

Open a connection and return a runtime.

#### Remarks [#remarks-1]

* 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](https://www.prisma.io/docs/orm/reference/sql-query-builder#executing-a-plan).
  * `runtime.execute(built)` runs a built query that returns no rows, such as an insert, and returns `{ affectedRows }`. See [Running a built query](https://www.prisma.io/docs/orm/reference/sql-query-builder#executing-a-plan).
  * `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](https://www.prisma.io/docs/orm/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-1]

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

#### Examples [#examples-1]

##### Connect and run a query [#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()` [#runtime]

Get the current runtime synchronously.

#### Remarks [#remarks-2]

* 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-2]

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

### `close()` [#close]

Close the client and release its pool.

#### Remarks [#remarks-3]

* 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-3]

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

#### Examples [#examples-2]

##### Close when finished [#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) [#await-using-automatic-disposal]

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

#### Remarks [#remarks-4]

* 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 [#examples-3]

##### Close automatically with `await using` [#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 [#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)` [#mongooptions]

Create a MongoDB client.

#### Remarks [#remarks-5]

* 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 [#options-1]

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](https://www.prisma.io/docs/orm/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-4]

| 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 schema segment: `db.orm.users`. The collection name is the model's `@@map` value, or the model name with a lowercase first letter when the model sets no `@@map`. The [example schema](https://www.prisma.io/docs/orm/reference/orm-client#example-schema) sets `@@map("users")` on `User`, which is why the key here is `users`. See the [ORM client reference](https://www.prisma.io/docs/orm/reference/orm-client).
* `.query` is the pipeline builder, which builds MongoDB aggregation pipelines. See the [pipeline builder reference](https://www.prisma.io/docs/orm/reference/pipeline-builder). `.raw` sends raw MongoDB commands. See the [raw queries reference](https://www.prisma.io/docs/orm/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 [#examples-4]

##### Create a client from a connection string [#create-a-client-from-a-connection-string-1]

```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` [#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()` [#connect-1]

Open a connection and return a runtime.

#### Remarks [#remarks-6]

* 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-5]

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

### `runtime()` [#runtime-1]

Get the runtime.

#### Remarks [#remarks-7]

* On MongoDB, `runtime()&#x60; returns a &#x2A;*`Promise<MongoRuntime>`**: 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-6]

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

### `runtime.query()` [#runtimequery]

Run a built query through the runtime.

#### Remarks [#remarks-8]

* `(await db.runtime()).query(built)` runs any built MongoDB query, including one built by the [pipeline builder](https://www.prisma.io/docs/orm/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-7]

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

#### Examples [#examples-5]

##### Run a query built by the pipeline builder [#run-a-pipeline-builder-plan]

```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-1]

Close the client.

#### Remarks [#remarks-9]

* 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-8]

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

#### Examples [#examples-6]

##### Use after close throws [#use-after-close-throws]

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

### `await using` (automatic disposal) [#await-using-automatic-disposal-1]

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

#### Remarks [#remarks-10]

* 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 [#examples-7]

##### Close automatically with `await using` [#close-automatically-with-await-using-1]

```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) [#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)` [#dbtransactioncallback]

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

#### Remarks [#remarks-11]

* 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](https://www.prisma.io/docs/orm/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](https://www.prisma.io/docs/orm/reference/error-reference).

#### Options [#options-2]

| 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-9]

| 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 [#examples-8]

##### Commit several writes atomically [#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 [#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 [#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` [#run-a-sql-builder-plan-with-txsql-and-txexecute]

```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({ label: 'tx-escape' });
>   return { rows: tx.query(tx.sql.public.tag.select('label').build()) };
> });
>
> 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)` [#withtransactionruntime-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 [#remarks-12]

* 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 [#options-3]

| 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-10]

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

#### Examples [#examples-9]

##### Commit two writes with `withTransaction` [#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 [#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 [#remarks-13]

* `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 [#options-4]

| 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 [#examples-10]

##### Commit manually, then release [#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 [#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()` [#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) [#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](https://www.prisma.io/docs/orm/fundamentals/transactions#transactions-on-mongodb) shows. The reading rules under [`AsyncIterableResult`](#asynciterableresult) apply to MongoDB too.

## Prepared statements (PostgreSQL) [#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)` [#runtimepreparedeclaration-callback]

Prepare a statement from a runtime.

#### Remarks [#remarks-14]

* 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](https://www.prisma.io/docs/orm/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 [#options-5]

| 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-11]

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

#### Examples [#examples-11]

##### Prepare and run a statement [#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 [#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)` [#dbpreparedeclaration-callback]

Prepare a statement on the client itself.

#### Remarks [#remarks-15]

* `db.prepare(...)` does the same thing as `runtime.prepare(...)`, but it passes you a SQL builder as the callback's first argument, so you do not need `db.sql`. The callback takes **two** arguments, `(sql, params)`. Build the query with `sql.public.<table>`.
* `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 [#options-6]

| Name          | Type                                 | Required | Description                                          |
| ------------- | ------------------------------------ | -------- | ---------------------------------------------------- |
| `declaration` | Object mapping param name to type id | Yes      | The statement's parameters.                          |
| `callback`    | `(sql, params) => SqlQueryPlan`      | Yes      | Builds the query; `sql` is the client's SQL builder. |

#### Return type [#return-type-12]

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

#### Examples [#examples-12]

##### Prepare off the client with the injected `sql` builder [#prepare-off-the-client-with-the-injected-sql-builder]

```typescript
const ps = await db.prepare({ label: 'pg/text@1' }, (sql, params) =>
  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' });
```

### `PreparedStatement.query(target, params)` [#preparedstatementquerytarget-params]

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

#### Remarks [#remarks-16]

* 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](https://www.prisma.io/docs/orm/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 [#options-7]

| 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-13]

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

#### Examples [#examples-13]

##### Run one prepared statement against a transaction and the runtime [#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 [#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` [#runtimeexecuteoptions]

Per-query options for cancellation.

#### Remarks [#remarks-17]

* `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 [#options-8]

| 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 [#examples-14]

##### A pre-aborted signal short-circuits [#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()` [#runtimetelemetry]

Read telemetry about the most recent query.

#### Remarks [#remarks-18]

* 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-14]

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

#### Examples [#examples-15]

##### Read telemetry before and after a query [#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` [#asynciterableresult]

[`all()`](https://www.prisma.io/docs/orm/reference/orm-client#all), [`createAll()`](https://www.prisma.io/docs/orm/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`](https://www.prisma.io/docs/orm/reference/orm-client#asynciterableresult) in the ORM client reference.

## Related pages

- [`Error reference`](https://www.prisma.io/docs/orm/reference/error-reference): Every structured error code Prisma ORM can emit, by namespace, with the condition that raises it.
- [`ORM client reference`](https://www.prisma.io/docs/orm/reference/orm-client): Reference for the Prisma ORM client's query, mutation, filter, and aggregate methods.
- [`Pipeline builder reference`](https://www.prisma.io/docs/orm/reference/pipeline-builder): Reference for the Prisma ORM MongoDB pipeline builder's stages, accumulators, expression helpers, and write methods.
- [`Raw queries reference`](https://www.prisma.io/docs/orm/reference/raw-queries): Reference for Prisma ORM raw queries: PostgreSQL raw SQL and MongoDB raw commands.
- [`SQL query builder reference`](https://www.prisma.io/docs/orm/reference/sql-query-builder): Reference for the Prisma ORM SQL query builder's select, mutation, and grouped query methods.