# Serverless driver

The Prisma Postgres serverless driver connects to hosted Prisma Postgres databases over HTTP and WebSockets. Use it with Prisma ORM through [`@prisma/adapter-ppg`](https://www.npmjs.com/package/@prisma/adapter-ppg), or query with raw SQL through [`@prisma/ppg`](https://www.npmjs.com/package/@prisma/ppg).

## [Choose a connection method](#choose-a-connection-method)

| Runtime                                                              | Recommended connection                            | Package                                                             |
| -------------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------- |
| Conventional Node.js or Bun runtime with PostgreSQL TCP support      | [Pooled TCP](/guides/database-connection-pooling) | `@prisma/adapter-pg` with `pg`                                      |
| Edge or TCP-constrained runtime with `fetch` and `WebSocket` support | Prisma Postgres serverless driver                 | `@prisma/adapter-ppg` with Prisma ORM, or `@prisma/ppg` for raw SQL |

Use the serverless driver when a conventional PostgreSQL TCP driver cannot run. It also supports streaming, pipelined queries, transactions, batch operations, SQL template literals, and custom type handling.

If your application currently uses a hosted `prisma+postgres://` Accelerate connection, follow [Connect to Prisma Postgres without Accelerate](/guides/migrating-off-accelerate-postgres-database-switch-from-accelerate).

## [Get your connection string](#get-your-connection-string)

The serverless driver accepts the direct Prisma Postgres connection-string format:

```title="Direct connection string"
postgres://identifier:key@db.prisma.io:5432/postgres?sslmode=require
```

It uses this value as a credential, then communicates with Prisma Postgres over HTTP and WebSockets. It does not open a TCP connection.

In the [Prisma Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=postgres), select your database, choose **Connect to your database**, generate a connection string, and copy the direct value. Do not construct the value by editing another connection string.

If you don't have a Prisma Postgres database, create one using the [`create-db` CLI](/guides/introduction-2-postgres-npx-create-db) tool:

::::tabs
:::tab{title="bun"}
```bash
bunx create-db@latest
```
:::

:::tab{title="pnpm"}
```bash title="Terminal"
pnpm dlx create-db@latest
```
:::

:::tab{title="yarn"}
```bash title="Terminal"
yarn dlx create-db@latest
```
:::

:::tab{title="npm"}
```bash title="Terminal"
npx create-db@latest
```

> \[!WARNING]
> Keep credentials server-side
>
> Store `DATABASE_URL` in server-side runtime secrets. Never commit it to source control or include it in code delivered to a browser.
:::
::::

:::callout{intent="warning"}
Keep credentials server-side

Store `DATABASE_URL` in server-side runtime secrets. Never commit it to source control or include it in code delivered to a browser.
:::

## [Installation](#installation)

Install the appropriate package based on your use case:

::::::tabs
:::::tab{title="With Prisma ORM"}
::::tabs
:::tab{title="bun"}
```bash
bun add @prisma/ppg @prisma/adapter-ppg
```
:::

:::tab{title="pnpm"}
```bash title="Terminal" 
pnpm add @prisma/ppg @prisma/adapter-ppg
```
:::

:::tab{title="yarn"}
```bash title="Terminal" 
yarn add @prisma/ppg @prisma/adapter-ppg
```
:::

:::tab{title="npm"}
```bash title="Terminal" 
npm install @prisma/ppg @prisma/adapter-ppg
```
:::
::::
:::::

:::tab{title="Raw SQL"}
````bash title="Terminal" 
    bun add @prisma/ppg
    ```
````
:::
::::::

## [Use with Prisma ORM](#use-with-prisma-orm) {#use-with-prisma-orm}

Use the [`PrismaPostgresAdapter`](https://www.npmjs.com/package/@prisma/adapter-ppg) to connect Prisma Client via the serverless driver.

When you generate Prisma Client for an edge runtime, set the generator's [`runtime`](/docs/orm/v7/prisma-schema/overview/generators#field-reference) to the deployment target. For example, use `workerd` for Cloudflare Workers, `vercel-edge` for Vercel Edge Functions, or `deno` for Deno:

```title="prisma/schema.prisma"
generator client {

  provider = "prisma-client"

  output   = "../generated/prisma"

  runtime  = "workerd"

}
```

Generate the client, then instantiate it with the serverless driver adapter:

```title="src/lib/prisma.ts"
import { PrismaClient } from "../../generated/prisma/client";

import { PrismaPostgresAdapter } from "@prisma/adapter-ppg";

const prisma = new PrismaClient({

  adapter: new PrismaPostgresAdapter({

    connectionString: process.env.DATABASE_URL!,

  }),

});

const users = await prisma.user.findMany();
```

## [Query with raw SQL](#query-with-raw-sql)

Use the `prismaPostgres()` high-level API for SQL template literals with automatic parameterization:

```title="src/lib/query.ts"
import { prismaPostgres, defaultClientConfig } from "@prisma/ppg";

const ppg = prismaPostgres(defaultClientConfig(process.env.DATABASE_URL!));

type User = { id: number; name: string; email: string };

const users = await ppg.sql<User>`

  SELECT * FROM users WHERE email = ${"user@example.com"}

`.collect();

console.log(users[0].name);
```

### [Stream results](#stream-results)

Results are returned as `CollectableIterator<T>`. Stream rows one at a time for constant memory usage, or collect all rows into an array:

```title="src/lib/stream.ts"
type User = { id: number; name: string; email: string };

// Stream rows one at a time (constant memory usage)

for await (const user of ppg.sql<User>`SELECT * FROM users`) {

  console.log(user.name);

}

// Or collect all rows into an array

const allUsers = await ppg.sql<User>`SELECT * FROM users`.collect();
```

### [Pipeline queries](#pipeline-queries)

Send multiple queries over a single WebSocket connection without waiting for responses. Queries are sent immediately and results arrive in FIFO order:

```title="src/lib/pipeline.ts"
import { client, defaultClientConfig } from "@prisma/ppg";

const cl = client(defaultClientConfig(process.env.DATABASE_URL!));

const session = await cl.newSession();

// Send all queries immediately (pipelined)

const [usersResult, ordersResult, productsResult] = await Promise.all([

  session.query("SELECT * FROM users"),

  session.query("SELECT * FROM orders"),

  session.query("SELECT * FROM products"),

]);

session.close();
```

Pipelining reduces network round trips by sending multiple queries before waiting for their responses. The effect on end-to-end latency depends on network conditions and query execution time.

### [Parameter streaming](#parameter-streaming)

Parameters over 1KB are automatically streamed without buffering in memory. For large binary parameters, you must use `boundedByteStreamParameter()` which creates a `BoundedByteStreamParameter` object that carries the total byte size, required by the PostgreSQL protocol:

```title="src/lib/upload.ts"
import { client, defaultClientConfig, boundedByteStreamParameter, BINARY } from "@prisma/ppg";

const cl = client(defaultClientConfig(process.env.DATABASE_URL!));

// Large binary data (e.g., file content)

const stream = getReadableStream(); // Your ReadableStream source

const totalSize = 1024 * 1024; // Total size must be known in advance

// Create a bounded byte stream parameter

const streamParam = boundedByteStreamParameter(stream, BINARY, totalSize);

// Automatically streamed - constant memory usage

await cl.query("INSERT INTO files (data) VALUES ($1)", streamParam);
```

For `Uint8Array` data, use `byteArrayParameter()`:

```title="src/lib/bytes.ts"
import { client, defaultClientConfig, byteArrayParameter, BINARY } from "@prisma/ppg";

const cl = client(defaultClientConfig(process.env.DATABASE_URL!));

const bytes = new Uint8Array([1, 2, 3, 4]);

const param = byteArrayParameter(bytes, BINARY);

await cl.query("INSERT INTO files (data) VALUES ($1)", param);
```

The `boundedByteStreamParameter()` function is provided by the `@prisma/ppg` library and requires the total byte size to be known in advance due to PostgreSQL protocol requirements.

### [Transactions and batch operations](#transactions-and-batch-operations)

Transactions automatically handle BEGIN, COMMIT, and ROLLBACK:

```title="src/lib/transaction.ts"
const result = await ppg.transaction(async (tx) => {

  await tx.sql.exec`INSERT INTO users (name) VALUES ('Alice')`;

  const users = await tx.sql<User>`SELECT * FROM users WHERE name = 'Alice'`.collect();

  return users[0].name;

});
```

Batch operations execute multiple statements in a single round-trip within an automatic transaction:

```title="src/lib/batch.ts"
const [users, affected] = await ppg.batch<[User[], number]>(

  { query: "SELECT * FROM users WHERE id < $1", parameters: [5] },

  { exec: "INSERT INTO users (name) VALUES ($1)", parameters: ["Charlie"] },

);
```

## [Type handling](#type-handling)

When using `defaultClientConfig()`, common PostgreSQL types are automatically parsed (`boolean`, `int2`, `int4`, `int8`, `float4`, `float8`, `text`, `varchar`, `json`, `jsonb`, `date`, `timestamp`, `timestamptz`):

```title="src/lib/types.ts"
import { prismaPostgres, defaultClientConfig } from "@prisma/ppg";

const ppg = prismaPostgres(defaultClientConfig(process.env.DATABASE_URL!));

// JSON/JSONB automatically parsed

const rows = await ppg.sql<{ data: { key: string } }>`

  SELECT '{"key": "value"}'::jsonb as data

`.collect();

console.log(rows[0].data.key); // "value"

// BigInt parsed to JavaScript BigInt

const bigints = await ppg.sql<{

  big: bigint;

}>`SELECT 9007199254740991::int8 as big`.collect();

// Dates parsed to Date objects

const dates = await ppg.sql<{

  created: Date;

}>`SELECT NOW() as created`.collect();
```

### [Custom parsers and serializers](#custom-parsers-and-serializers)

Extend or override the type system with custom parsers (by PostgreSQL OID) and serializers (by type guard):

```title="src/lib/custom-parser.ts"
import { client, defaultClientConfig } from "@prisma/ppg";

import type { ValueParser } from "@prisma/ppg";

// Custom parser for UUID type

const uuidParser: ValueParser<string | null> = {

  oid: 2950,

  parse: (value) => (value ? value.toUpperCase() : null),

};

const config = defaultClientConfig(process.env.DATABASE_URL!);

const cl = client({

  ...config,

  parsers: [...(config.parsers ?? []), uuidParser], // Append to defaults

});
```

For custom serializers, place them before defaults so they take precedence:

```title="src/lib/custom-serializer.ts"
import { client, defaultClientConfig } from "@prisma/ppg";

import type { ValueSerializer } from "@prisma/ppg";

class Point {

  constructor(

    public x: number,

    public y: number,

  ) {}

}

const pointSerializer: ValueSerializer<Point> = {

  supports: (value: unknown): value is Point => value instanceof Point,

  serialize: (value: Point) => `(${value.x},${value.y})`,

};

const config = defaultClientConfig(process.env.DATABASE_URL!);

const cl = client({

  ...config,

  serializers: [pointSerializer, ...(config.serializers ?? [])], // Your serializer first

});

await cl.query("INSERT INTO locations (point) VALUES ($1)", new Point(10, 20));
```

See the [npm package documentation](https://www.npmjs.com/package/@prisma/ppg) for more details.

## [Platform compatibility](#platform-compatibility)

The driver works in server-side environments with `fetch` and `WebSocket` APIs:

| Platform              | HTTP Transport | WebSocket Transport |
| --------------------- | -------------- | ------------------- |
| Cloudflare Workers    | ✅              | ✅                   |
| Vercel Edge Functions | ✅              | ✅                   |
| AWS Lambda            | ✅              | ✅                   |
| Deno Deploy           | ✅              | ✅                   |
| Bun                   | ✅              | ✅                   |
| Node.js 18+           | ✅              | ✅                   |

The package can run in browser environments, but a database connection string is a server credential. Do not use the driver in browser-delivered code. Route browser requests through a server-side endpoint instead.

## [Transport modes](#transport-modes)

- **HTTP transport (stateless):** Each query is an independent HTTP request. Best for simple queries and edge functions.
- **WebSocket transport (stateful):** Persistent connection for multiplexed queries. Best for transactions, pipelining, and multiple queries. Create a session with `client().newSession()`.

## [API overview](#api-overview)

### [`prismaPostgres(config)`](#prismapostgresconfig)

High-level API with SQL template literals, transactions, and batch operations. Recommended for most use cases.

### [`client(config)`](#clientconfig)

Low-level API with explicit parameter passing and session management. Use when you need fine-grained control.

See the [npm package](https://www.npmjs.com/package/@prisma/ppg) for complete API documentation.

## [Error handling](#error-handling)

Structured error types are provided: `DatabaseError`, `HttpResponseError`, `WebSocketError`, `ValidationError`.

```title="src/lib/errors.ts"
import { DatabaseError } from "@prisma/ppg";

try {

  await ppg.sql`SELECT * FROM invalid_table`.collect();

} catch (error) {

  if (error instanceof DatabaseError) {

    console.log(error.code);

  }

}
```

## [Connection pooling](#connection-pooling)

The serverless driver uses Prisma Postgres connection pooling by default and requires no additional pool configuration. See the [Prisma Postgres regions](/guides/more-2-faq#what-regions-is-prisma-postgres-available-in).

## [Limitations](#limitations)

- Requires a Prisma Postgres instance and does not work with [Local Postgres](/guides/local-development-2-postgres) databases

## [Learn more](#learn-more)

- [`@prisma/ppg` npm package](https://www.npmjs.com/package/@prisma/ppg)
- [prisma/ppg-client GitHub repository](https://github.com/prisma/ppg-client)
- [Prisma Postgres documentation](/guides/introduction-2-postgres)

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