The answer. A Worker runs in hundreds of locations, and a Postgres connection is a TCP handshake, a TLS handshake and an auth round trip before the first query. Hyperdrive puts a connection pool near your database and a lightweight endpoint near the Worker: the driver in your code connects to the endpoint (fast), the pool holds warm connections to the origin (already open), and the query crosses the network once. You keep your driver and your SQL; the only change is the connection string, which Hyperdrive generates from a binding. Eligible reads are cached by default (60 s), which is a second speedup and a correctness question: caching does not invalidate on your writes, so reads that must see the latest row use a second, cache-disabled configuration of the same database.
The pattern.
npx wrangler hyperdrive create app-db --connection-string="postgres://user:pass@pooler.example.com:5432/postgres"
npx wrangler hyperdrive create app-db-fresh --connection-string="..." --caching-disabled
{ "compatibility_flags": ["nodejs_compat"],
"hyperdrive": [{ "binding": "HYPERDRIVE", "id": "<id>" }, { "binding": "HYPERDRIVE_FRESH", "id": "<id-fresh>" }] }
import postgres from "postgres";
export default { async fetch(request, env, ctx) {
const sql = postgres(env.HYPERDRIVE.connectionString); // create per request, not at module scope
const rows = await sql`select id, title from articles where site_id = ${siteId} order by id desc limit 20`;
ctx.waitUntil(sql.end());
return Response.json(rows);
} } satisfies ExportedHandler<Env>;
Watch out.
- Hyperdrive's own pool runs in transaction mode. Give it a session-mode host (Supabase's pooler on 5432, not 6543); the direct Supabase host is IPv6-only and is not the right target either.
- Read-after-write through the cached binding returns the pre-write row for up to the cache TTL. Auth, permissions and "did my update land" queries go through the fresh binding.
- Every Worker location shares the pool, so origin connection counts are bounded by the configuration, not by your traffic. Size the origin's
max_connectionsfor the pool, not for the Workers.
Related: session-pooler-5432-for-ddl-direct-host-ipv6-only · d1-sessions-api-bookmark-for-read-replicas · kv-cache-upstream-json-with-ttl-and-stale-fallback