Next.js
Tested with: Next.js 15.5 · React 19 · pg 8.13 · ioredis 5.4 · @libsql/client 0.14
A server-only database module
Section titled “A server-only database module”import "server-only";import { Pool } from "pg";
const globalForPg = globalThis as unknown as { pool?: Pool };
export const pool = globalForPg.pool ?? new Pool({ connectionString: process.env.DATABASE_URL, // pooled endpoint on serverless, direct on a long-running server max: process.env.VERCEL ? 3 : 10, ssl: { rejectUnauthorized: true }, // verify-full: chain + hostname via the OS trust store });
if (process.env.NODE_ENV !== "production") globalForPg.pool = pool;import "server-only" guarantees the module (and the credential) never reaches a client bundle. Use it from Server
Components, Route Handlers and Server Actions:
import { pool } from "@/lib/db";
export default async function Page() { const { rows } = await pool.query<{ now: string }>("select now()"); return <p>Server time: {rows[0]?.now}</p>;}Runtime
Section titled “Runtime”Database access needs the Node runtime. Add export const runtime = "nodejs" to routes that touch Postgres,
Valkey or FerretDB; only libSQL (@libsql/client/web) works on the Edge runtime.
Caching with Valkey
Section titled “Caching with Valkey”import "server-only";import Redis from "ioredis";
const g = globalThis as unknown as { redis?: Redis };export const redis = g.redis ?? new Redis(process.env.VALKEY_URL!, { tls: { rejectUnauthorized: true }, maxRetriesPerRequest: 2 });if (process.env.NODE_ENV !== "production") g.redis = redis;VALKEY_URL is the rediss:// URI from the instance’s Connect tab.
Environment
Section titled “Environment”| Variable | Value | Where |
|---|---|---|
DATABASE_URL | pooled URI with sslmode=verify-full | runtime |
DIRECT_URL | direct URI | prisma migrate / drizzle-kit in CI |
VALKEY_URL | rediss://app:[email protected]:6379/0 | runtime |