| title | Serverless Driver |
|---|---|
| icon | Zap |
| tag | TypeScript |
| description | Use @neondatabase/serverless to connect from edge runtimes and serverless functions through PgBeam with HTTP or WebSocket transports. |
Connect from edge runtimes and serverless functions using the
@neondatabase/serverless driver. PgBeam supports both the HTTP query endpoint
and the WebSocket wire protocol. Your neon() / Pool call sites and every
query stay exactly as they are.
import { neonConfig } from "@neondatabase/serverless";
// HTTP /sql endpoint: keep the project subdomain, append /sql
neonConfig.fetchEndpoint = (host) => `https://${host}/sql`;
// WebSocket /v2 endpoint (Pool / Client)
neonConfig.wsProxy = (host) => `${host}/v2`;These two lines are the only addition. Your query call sites do not change.
(On Neon's own *.neon.tech hosts the defaults work; PgBeam needs the override
because its hostnames are not *.neon.tech.)
<Tabs items={["npm", "pnpm", "yarn", "bun"]}>
<Tab value="npm">
```bash
npm install @neondatabase/serverless
```
</Tab>
<Tab value="pnpm">
```bash
pnpm add @neondatabase/serverless
```
</Tab>
<Tab value="yarn">
```bash
yarn add @neondatabase/serverless
```
</Tab>
<Tab value="bun">
```bash
bun add @neondatabase/serverless
```
</Tab>
</Tabs>
For Node.js environments using WebSocket transport, also install `ws`:
```bash
npm install ws
```
Set `neonConfig` once, before any query runs (see the warning above for why
this is required). In Node.js, also set the WebSocket constructor.
```ts title="neon-config.ts"
import { neonConfig } from "@neondatabase/serverless";
neonConfig.fetchEndpoint = (host) => `https://${host}/sql`;
neonConfig.wsProxy = (host) => `${host}/v2`;
// Required for the WebSocket Pool/Client paths against PgBeam: PgBeam
// authenticates with SCRAM, but the driver's default pipelining optimistically
// sends a cleartext password before it sees the auth challenge, which hangs the
// connection. Disabling pipelining makes the driver complete the SCRAM handshake.
neonConfig.pipelineConnect = false;
// Node.js only: provide a WebSocket implementation for the Pool/Client paths
// import ws from "ws";
// neonConfig.webSocketConstructor = ws;
```
Best for one-shot queries from edge/serverless functions. Each call is a single
HTTP request. No persistent connection required.
```ts title="query.ts"
import "./neon-config"; // applies neonConfig.fetchEndpoint
import { neon } from "@neondatabase/serverless";
const sql = neon("postgresql://user:pass@abc.proxy.pgbeam.app/mydb");
const rows = await sql`SELECT * FROM users WHERE active = true`;
```
Best for interactive transactions or session-level features. Uses the
PostgreSQL wire protocol over WebSocket.
<Callout type="warn" title="Set neonConfig.pipelineConnect = false for WebSocket">
With the driver's default configuration, the WebSocket `Pool` / `Client`
connection **hangs** against PgBeam. PgBeam authenticates with SCRAM, and the
driver's default `pipelineConnect: "password"` optimistically pipelines a
cleartext password before it reads the server's auth challenge, so the SCRAM
handshake never completes. Set `neonConfig.pipelineConnect = false` (already
in the `neon-config.ts` above) so the driver waits for the challenge and
completes SCRAM. The HTTP `neon()` path is unaffected; if you only need
one-shot queries, it works with just `fetchEndpoint` set.
</Callout>
<Tabs items={["Edge runtime", "Node.js"]}>
<Tab value="Edge runtime">
```ts title="db.ts"
import "./neon-config"; // applies neonConfig.wsProxy
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({
connectionString: "postgresql://user:pass@abc.proxy.pgbeam.app/mydb",
});
const { rows } = await pool.query("SELECT * FROM users");
```
</Tab>
<Tab value="Node.js">
```ts title="db.ts"
import { Pool, neonConfig } from "@neondatabase/serverless";
import ws from "ws";
neonConfig.fetchEndpoint = (host) => `https://${host}/sql`;
neonConfig.wsProxy = (host) => `${host}/v2`;
neonConfig.pipelineConnect = false; // required for SCRAM (see callout above)
neonConfig.webSocketConstructor = ws;
const pool = new Pool({
connectionString: "postgresql://user:pass@abc.proxy.pgbeam.app/mydb",
});
const { rows } = await pool.query("SELECT * FROM users");
```
</Tab>
</Tabs>
```ts
const result = await sql`SELECT 1 AS ok`;
console.log(result); // [{ ok: 1 }]
```
If this returns results, the serverless driver is connected through PgBeam.
HTTP (neon()) |
WebSocket (Pool / Client) |
|
|---|---|---|
| Best for | One-shot queries, edge functions | Transactions, session features |
| Connection | Stateless HTTP request | Persistent WebSocket |
| Cold start | None | WebSocket handshake + TLS |
| Transactions | sql.transaction([...]) |
BEGIN / COMMIT via client |
| Pipelining | Automatic (batched in one request) | PostgreSQL wire protocol |
| Max payload | ~10 MB response | Unlimited streaming |
The serverless driver works in any runtime with fetch (for HTTP) or
WebSocket (for WS):
| Runtime | HTTP | WebSocket | Notes |
|---|---|---|---|
| Vercel Edge Functions | Yes | Yes | Built-in WebSocket support |
| Cloudflare Workers | Yes | Yes | Built-in WebSocket support |
| Deno Deploy | Yes | Yes | Built-in WebSocket support |
| Bun | Yes | Yes | Built-in WebSocket support |
| Node.js | Yes | Yes | Requires ws package |
Use the serverless driver adapters with Drizzle for type-safe queries:
The same neonConfig setup applies. Drizzle wraps the Neon driver, so once the
endpoint is pointed at PgBeam, your Drizzle schema and queries are unchanged.
<Tabs items={["HTTP (drizzle-orm/neon-http)", "WebSocket (drizzle-orm/neon-serverless)"]}> ```ts title="db.ts" import "./neon-config"; // applies neonConfig.fetchEndpoint import { neon } from "@neondatabase/serverless"; import { drizzle } from "drizzle-orm/neon-http";
const sql = neon("postgresql://user:pass@abc.proxy.pgbeam.app/mydb");
const db = drizzle(sql);
const users = await db.select().from(usersTable);
```
const pool = new Pool({
connectionString: "postgresql://user:pass@abc.proxy.pgbeam.app/mydb",
});
const db = drizzle(pool);
const users = await db.select().from(usersTable);
```
<Tabs items={["HTTP", "WebSocket"]}>
Use sql.transaction() to run multiple statements in a single HTTP request:
```ts title="HTTP transactions"
import { neon } from "@neondatabase/serverless";
const sql = neon("postgresql://user:pass@abc.proxy.pgbeam.app/mydb");
const results = await sql.transaction([
sql`UPDATE accounts SET balance = balance - 100 WHERE id = 1`,
sql`UPDATE accounts SET balance = balance + 100 WHERE id = 2`,
]);
```
```ts title="WebSocket transactions"
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({
connectionString: "postgresql://user:pass@abc.proxy.pgbeam.app/mydb",
});
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
await client.query("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
await client.query("COMMIT");
} catch (e) {
await client.query("ROLLBACK");
throw e;
} finally {
client.release();
}
```
PgBeam matches the @neondatabase/serverless type contract over the HTTP
endpoint. In particular, bigint (int8), numeric, and money are returned
as strings, not numbers: a JavaScript Number cannot represent integers
above 2^53 or arbitrary-precision decimals without silent loss. Wrap them with
BigInt(...) or a decimal library as needed.
const [row] = await sql`SELECT 9223372036854775807::bigint AS big`;
row.big; // "9223372036854775807" (string)
BigInt(row.big); // 9223372036854775807njson / jsonb are parsed into objects, and arrays into JS arrays, exactly as
the Neon driver does.
SQL annotations work the same with the serverless driver:
import { neon } from "@neondatabase/serverless";
const sql = neon("postgresql://user:pass@abc.proxy.pgbeam.app/mydb");
// Cache for 5 minutes
const categories =
await sql`/* @pgbeam:cache maxAge=300 */ SELECT * FROM categories`;
// Route to read replica
const stats = await sql`/* @pgbeam:replica */ SELECT count(*) FROM orders`;
// Combine both
const products =
await sql`/* @pgbeam:replica */ /* @pgbeam:cache maxAge=60 */ SELECT * FROM products`;See Caching and Read Replicas for details.
| Issue | Cause | Fix |
|---|---|---|
Requests hit api.proxy.pgbeam.app / 404 / wrong project |
neonConfig.fetchEndpoint not set; driver rewrote the host to api. |
Set neonConfig.fetchEndpoint so requests target the /sql path (see Setup step 2) |
WebSocket is not defined |
Node.js missing WS constructor | Install ws and set neonConfig.webSocketConstructor = ws |
WebSocket Pool / Client connects then hangs, no error |
Default pipelineConnect: "password" pipelines a cleartext password, but PgBeam uses SCRAM |
Set neonConfig.pipelineConnect = false. If you only need one-shot queries, use the HTTP neon() path instead |
fetch failed on HTTP endpoint |
Network/firewall blocking HTTPS, or fetchEndpoint not pointed at /sql |
Verify the proxy hostname resolves, port 443 is open, and fetchEndpoint is set |
| Slow cold starts with WebSocket | TLS + WS handshake on each invocation | Use HTTP transport for stateless queries |
connection terminated |
Idle timeout exceeded | Use connection pooling or reconnect on error |
- Connection Pooling: Pool modes and sizing guidance
- Caching: TTL, SWR, cache rules, and SQL annotations
- Read Replicas: Replica setup and routing
- @neondatabase/serverless on npm: Driver documentation