[ PROMPT_NODE_23611 ]
Neon Serverless
[ SKILL_DOCUMENTATION ]
# Neon Serverless Driver
Patterns and best practices for connecting to Neon databases in serverless environments using the `@neondatabase/serverless` driver. The driver connects over **HTTP** for fast, single queries or **WebSockets** for `node-postgres` compatibility and interactive transactions.
For official documentation:
```bash
curl -H "Accept: text/markdown" https://neon.tech/docs/serverless/serverless-driver
```
## Installation
```bash
# Using npm
npm install @neondatabase/serverless
# Using JSR
bunx jsr add @neon/serverless
```
**Note:** Version 1.0.0+ requires **Node.js v19 or later**.
For projects that depend on `pg` but want to use Neon's WebSocket-based connection pool:
```json
"dependencies": {
"pg": "npm:@neondatabase/serverless@^0.10.4"
},
"overrides": {
"pg": "npm:@neondatabase/serverless@^0.10.4"
}
```
## Connection String
Always use environment variables:
```typescript
// For HTTP queries
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
// For WebSocket connections
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
```
**Never hardcode credentials:**
```typescript
// AVOID
const sql = neon("postgres://username:[email protected]/neondb");
```
## HTTP Queries with `neon` function
Ideal for simple, "one-shot" queries in serverless/edge environments. Uses HTTP `fetch` - fastest method for single queries.
### Parameterized Queries
Use tagged template literals for safe parameter interpolation:
```typescript
const [post] = await sql`SELECT * FROM posts WHERE id = ${postId}`;
```
For manually constructed queries:
```typescript
const [post] = await sql.query("SELECT * FROM posts WHERE id = $1", [postId]);
```
**Never concatenate user input:**
```typescript
// AVOID: SQL Injection Risk
const [post] = await sql("SELECT * FROM posts WHERE id = " + postId);
```
### Configuration Options
```typescript
// Return rows as arrays instead of objects
const sqlArrayMode = neon(process.env.DATABASE_URL!, { arrayMode: true });
const rows = await sqlArrayMode`SELECT id, title FROM posts`;
// rows -> [[1, "First Post"], [2, "Second Post"]]
// Get full results including row count and field metadata
const sqlFull = neon(process.env.DATABASE_URL!, { fullResults: true });
const result = await sqlFull`SELECT * FROM posts LIMIT 1`;
// result -> { rows: [...], fields: [...], rowCount: 1, ... }
```
## WebSocket Connections with `Pool` and `Client`
Use for `node-postgres` compatibility, interactive transactions, or session support.
### WebSocket Configuration
For Node.js v21 and earlier:
```typescript
import { Pool, neonConfig } from "@neondatabase/serverless";
import ws from "ws";
// Required for Node.js {
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
try {
const { rows } = await pool.query("SELECT * FROM users");
return new Response(JSON.stringify(rows));
} catch (err) {
console.error(err);
return new Response("Database error", { status: 500 });
} finally {
ctx.waitUntil(pool.end());
}
};
```
**Avoid** creating a global `Pool` instance outside the handler.
## Transactions
### HTTP Transactions
For running multiple queries in a single, non-interactive transaction:
```typescript
const [newUser, newProfile] = await sql.transaction(
[
sql`INSERT INTO users(name) VALUES(${name}) RETURNING id`,
sql`INSERT INTO profiles(user_id, bio) VALUES(${userId}, ${bio})`,
],
{
isolationLevel: "ReadCommitted",
readOnly: false,
},
);
```
### Interactive Transactions
For complex transactions with conditional logic:
```typescript
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
const client = await pool.connect();
try {
await client.query("BEGIN");
const {
rows: [{ id }],
} = await client.query("INSERT INTO users(name) VALUES($1) RETURNING id", [
name,
]);
await client.query("INSERT INTO profiles(user_id, bio) VALUES($1, $2)", [
id,
bio,
]);
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
await pool.end();
}
```
## Environment-Specific Optimizations
```javascript
// For Vercel Edge Functions, specify nearest region
export const config = {
runtime: "edge",
regions: ["iad1"], // Region nearest to your Neon DB
};
// For Cloudflare Workers, consider using Hyperdrive
// https://neon.tech/blog/hyperdrive-neon-faq
```
## ORM Integration
For Drizzle ORM integration with the serverless driver, see `neon-drizzle.md`.
### Prisma
```typescript
import { neonConfig } from "@neondatabase/serverless";
import { PrismaNeon, PrismaNeonHTTP } from "@prisma/adapter-neon";
import { PrismaClient } from "@prisma/client";
import ws from "ws";
const connectionString = process.env.DATABASE_URL;
neonConfig.webSocketConstructor = ws;
// HTTP adapter
const adapterHttp = new PrismaNeonHTTP(connectionString!, {});
export const prismaClientHttp = new PrismaClient({ adapter: adapterHttp });
// WebSocket adapter
const adapterWs = new PrismaNeon({ connectionString });
export const prismaClientWs = new PrismaClient({ adapter: adapterWs });
```
### Kysely
```typescript
import { Pool } from "@neondatabase/serverless";
import { Kysely, PostgresDialect } from "kysely";
const dialect = new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
});
const db = new Kysely({ dialect });
```
**NOTE:** Do not pass the `neon()` function to ORMs that expect a `node-postgres` compatible `Pool`.
## Error Handling
```javascript
// Pool error handling
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
pool.on("error", (err) => {
console.error("Unexpected error on idle client", err);
process.exit(-1);
});
// Query error handling
try {
const [post] = await sql`SELECT * FROM posts WHERE id = ${postId}`;
if (!post) {
return new Response("Not found", { status: 404 });
}
} catch (err) {
console.error("Database query failed:", err);
return new Response("Server error", { status: 500 });
}
```
Source: claude-code-templates (MIT). See About Us for full credits.