Getting StartedConnect your framework
Node: pg and postgres.js
Plain Node Postgres drivers against CapyDB, no ORM required.
Environment
DATABASE_URL="postgres://user:password@host:6432/db?sslmode=require"
DATABASE_DIRECT_URL="postgres://user:password@host:5432/db?sslmode=require"node-postgres (pg)
import pg from 'pg'
const pool = new pg.Pool({
connectionString: process.env.DATABASE_URL,
max: 10,
})
const { rows } = await pool.query('SELECT now()')A pg.Pool on top of the pooled URL is fine — the client-side pool keeps connections warm per process, the server-side pooler multiplexes across processes. Keep max modest; the plan's connection budget is shared by everything you run.
postgres.js
import postgres from 'postgres'
const sql = postgres(process.env.DATABASE_URL!, { prepare: false })
const rows = await sql`SELECT now()`prepare: false is required on the pooled URL; named prepared statements do not survive transaction pooling. On the direct URL you can leave it on.
Long-lived workers
A worker that holds one steady connection (queue consumer, cron process) can use the direct URL — that is what it is for. Just count those connections against the plan budget (15/30/60 for Vibe/Ship/Business).
Pitfalls
SSL connection required: keepsslmode=requirein the URL.too many connections: lower the client poolmax, move app traffic to the pooled URL, or check Observability for what is holding connections.LISTEN/NOTIFYand session state do not work through the transaction pooler — use the direct URL for those.