2026-06-01infra

Request-scoped DB connections via Cloudflare Hyperdrive

  • Change: Reworked the database connection layer to stop sharing a pg.Pool across requests on Cloudflare Workers. lib/db.ts now resolves a Drizzle instance per request — keyed by the request's ExecutionContext via getCloudflareContext() — backed by a Cloudflare Hyperdrive binding (env.HYPERDRIVE.connectionString). A globalThis-cached fallback pool (reading DATABASE_URL/POSTGRES_URL) is retained for next dev, next build, and scripts. The exported db is now a transparent Proxy so existing import { db } call sites are unchanged. Added a hyperdrive binding to wrangler.jsonc and initOpenNextCloudflareForDev() in next.config.ts so the binding is present in local dev.
  • Why: On Workers, a TCP socket opened in one request cannot be reused by a later request — the globally cached pool handed stale, cross-request connections to new requests, which hung the Worker until the runtime canceled it ("detected that your Worker's code had hung"). Per-request connections eliminate the reuse, and Hyperdrive provides edge-side connection pooling so the per-request connect stays cheap.
  • Affected Modules: lib/db.ts, next.config.ts, wrangler.jsonc, cloudflare-env.d.ts
  • Trade-offs:
    • Pro: Removes the request-hang failure mode; Hyperdrive pools connections at the edge and lowers DB round-trip latency; call sites are untouched thanks to the proxy.
    • Con: Requires a one-time Hyperdrive provisioning step (wrangler hyperdrive create) and a real id in wrangler.jsonc; the Proxy indirection and per-request pool lookup add a small amount of overhead and conceptual complexity over a plain singleton.