After this lesson you'll be able to bind a Hyperdrive config to a Worker and query your existing regional Postgres or MySQL database from the edge with a standard driver, without rewriting your database or your queries.
Hyperdrive is a connection-pooling and caching layer that sits between your Workers and a regional database you already run — RDS, Cloud SQL, Supabase, PlanetScale, Neon, your own Postgres box, whatever. It doesn't move your data or replace your database. It solves one specific problem: Workers run as short-lived isolates distributed globally, and short-lived distributed compute is a terrible client for a database designed to be reached by a small number of long-lived, geographically-local connections. Hyperdrive makes an existing single-region database reachable from Workers everywhere without you migrating anything.
The core problem: a Postgres/MySQL connection is expensive to establish (TCP handshake, TLS, auth) and databases cap how many concurrent connections they'll hold open — often in the low hundreds. A traditional server keeps a warm connection pool for the lifetime of the process. A Worker isolate has no such lifetime; it can spin up per-request, in any of Cloudflare's data centers, with no persistent memory between invocations. If every Worker request opened its own fresh connection to a database in, say, `us-east-1`, you'd get two compounding problems: (1) the round-trip latency from a Worker in Tokyo to a database in Virginia dominates every query, and (2) at any real scale you'd exhaust the database's connection limit almost immediately, since there's no shared pool between isolates.
Hyperdrive fixes both. It maintains a real, warm connection pool to your database from Cloudflare's network, close to the database's region. Your Worker doesn't connect to the database directly — it connects to Hyperdrive, which is fast to reach from any Cloudflare location because Hyperdrive itself is distributed at the edge, and Hyperdrive reuses its already-open pooled connections to actually talk to your database. You still write normal SQL with a normal driver; only the connection string's target changes.
Request flow:
Worker (any edge location)
→ env.HYPERDRIVE.connectionString (fast: Hyperdrive is on Cloudflare's network)
→ Hyperdrive's pooled, warm connection
→ your Postgres/MySQL database (single region, e.g. us-east-1)
On top of pooling, Hyperdrive adds query caching: read queries it judges cacheable are served from Cloudflare's cache without touching your database at all, which is "default on" for eligible queries. This is separate from pooling — pooling fixes connection overhead for every query, caching skips the database entirely for repeated reads.
Hyperdrive itself is generous and mostly free:
| Plan | Query allowance |
|---|---|
| Free plan | 100,000 queries / day (resets 00:00 UTC); requests beyond this fail |
| Workers Paid plan | Unlimited queries, no extra charge for pooling or caching |
Every database operation counts as one query toward that allowance — SELECT, INSERT/UPDATE/DELETE, and DDL alike — and a cached read counts the same as an uncached one. There's no separate Cloudflare charge for data transfer/egress through Hyperdrive. Critically, Hyperdrive's pricing only covers Cloudflare's side — you still pay your database provider (RDS compute/storage, Supabase's plan tier, PlanetScale's row reads/writes, etc.) exactly as you would without Hyperdrive in front of it. One documented exception: PlanetScale Postgres/MySQL databases provisioned and billed directly through Cloudflare show up as a separate line item on your Cloudflare invoice at PlanetScale's standard rates. Treat these as a snapshot — confirm current numbers on the live pricing page linked below.
Create a Hyperdrive configuration pointing at your existing Postgres connection string:
npx wrangler hyperdrive create my-postgres-config \
--connection-string="postgres://user:password@HOSTNAME_OR_IP:5432/database_name"
This returns an id. Add it to wrangler.jsonc as a binding — the Worker never sees your raw connection string, only the binding:
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-01-01",
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "<YOUR_HYPERDRIVE_ID>"
}
]
}
Install a standard Postgres driver — Hyperdrive works with the ones you already know, like pg:
npm i pg
npm i -D @types/pg
Query it from the Worker exactly as you would from any Node server, using env.HYPERDRIVE.connectionString in place of your own connection string:
import { Client } from "pg";
export interface Env {
HYPERDRIVE: Hyperdrive;
}
export default {
async fetch(request, env, ctx): Promise<Response> {
const client = new Client({ connectionString: env.HYPERDRIVE.connectionString });
try {
await client.connect();
const result = await client.query(
"SELECT id, name, price FROM products WHERE active = $1 LIMIT 20",
[true]
);
// Close the connection without blocking the response —
// Hyperdrive still keeps the underlying pool warm.
ctx.waitUntil(client.end());
return Response.json(result.rows);
} catch (err) {
console.error(err);
return Response.json(
{ error: err instanceof Error ? err.message : String(err) },
{ status: 500 }
);
}
},
} satisfies ExportedHandler<Env>;
Deploy with npx wrangler deploy. Local development works too: wrangler dev connects through Hyperdrive using the same binding, so you're testing against the real database via the real pooling path, not a mock.
Cloudflare Docs — Hyperdrive for the overview and architecture; the firewall and networking configuration page for the IP allow-listing and Tunnel/VPC options; the pricing page for current query limits and costs.
Connection pooling — Hyperdrive keeps warm, pre-established connections to your database so a Worker doesn't pay handshake/TLS/auth cost on every request — and query caching, which is the one that can skip the database entirely by serving eligible read queries straight from Cloudflare's cache.