sellIntegration
database

Connect a Worker to existing Postgres via Hyperdrive

You already have a Postgres. Hyperdrive makes it fast and safe to query from Workers — no migration needed.

60sDefault cache TTL
0DB migrations
PoolingConnection reuse
FreeWorks on Free plan
insights

What are we wiring?

You have a regional Postgres already running — maybe on Neon, Supabase, AWS RDS, or your own server. It lives in one place. Your Workers, on the other hand, run in hundreds of cities. This guide wires a Worker to that database through Hyperdrive, so every request reuses warm connections and reads from cache instead of paying full latency each time.

Hyperdrive sits in the middle as a connection pooler plus query cache. Your code still uses a normal Postgres driver — the only change is that it connects to env.HYPERDRIVE.connectionString instead of your database's real URL.

schemaMany Worker calls share one pool

Cache hit returns instantly

Worker call 1

Hyperdrive

Worker call 2

Worker call 3

Connection pool: shared warm links

Query cache: remembers hot SELECTs

Your existing Postgres

fast_forward

Think of it like…

A taxi rank outside your database. Instead of every Worker calling a brand-new cab and waiting for it to arrive (a fresh connection), Hyperdrive keeps a few engines running at the curb and remembers popular destinations — so most riders leave immediately.

account_tree

Who does what?

dns

Your Worker

Runs globally and stays stateless. It opens a client against env.HYPERDRIVE.connectionString and runs SQL.

hub

Hyperdrive

The pooling + caching layer in front of your DB. Reuses connections and serves cached reads.

database

Your Postgres

Still the single source of truth. Schema, data, and provider all stay exactly as they are.

vpn_key

Connection string

Stored inside the Hyperdrive config, not in your Worker code — your DB password never ships in the bundle.

schemaHow one query is served

Yes and cached

No or not cached

Worker receives request

Is the query cacheable?

Return straight from cache

Borrow a warm link from the pool

Postgres runs the query

Return rows and fill the cache

Respond to user

help

Why not connect directly?

A direct connection from a far-away Worker pays a TCP + TLS + auth handshake every single time, and serverless invocations can quickly exhaust your database's connection limit. Pooling reuses warm links; caching skips the round-trip entirely for repeated reads.

swap_vert

Without vs with Hyperdrive

The diagram below shows the same workload twice. The top half connects directly and rebuilds a connection on every request. The bottom half goes through Hyperdrive and reuses a warm, pooled link — often answering straight from cache.

schemaConnection lifecycle comparison
"Postgres""Hyperdrive""Worker""User""Postgres""Hyperdrive""Worker""User"Without Hyperdrive - new connection every time (slow)With Hyperdrive - pooled + cached (fast)Request AOpen new connection + TLS handshakeConnection readySELECT queryrowsResponse (slower)Request BQuery via warm pooled linkCache hit or shared connectionResponse (faster)
speed

Where the time goes

On a cold direct connection, the handshake can cost several round-trips before a single byte of data moves. Hyperdrive pays that cost once and shares the result across many requests.

construction

Wire it up

Five steps: create a Hyperdrive config pointing at your DB, add the binding to wrangler.toml, install a Postgres driver, query through env.HYPERDRIVE.connectionString, and deploy.

  1. Create a Hyperdrive config

    Hand Hyperdrive your existing connection string. Wrangler prints an id — copy it for the next step.

    bash
    npx wrangler hyperdrive create my-postgres \
      --connection-string="postgres://user:password@db.example.com:5432/appdb"
  2. Add the [[hyperdrive]] binding

    Paste the id into wrangler.toml. The nodejs_compat flag is required, because Postgres drivers depend on Node APIs.

    toml
    name = "my-worker"
    main = "src/index.js"
    compatibility_date = "2024-09-23"
    compatibility_flags = ["nodejs_compat"]
    
    [[hyperdrive]]
    binding = "HYPERDRIVE"
    id = "<paste-the-id-from-step-1>"
  3. Install a Postgres driver

    We use postgres.js here; the node-postgres pg package works too.

    bash
    npm i postgres
  4. Query through Hyperdrive

    Connect to env.HYPERDRIVE.connectionString — never the real DB URL. Keep a small per-isolate pool and close it in the background.

    js
    import postgres from "postgres";
    
    export default {
      async fetch(request, env, ctx) {
        // Connect to Hyperdrive, not the database directly
        const sql = postgres(env.HYPERDRIVE.connectionString, {
          max: 5,            // small pool per Worker isolate
          fetch_types: false // skip an extra round-trip
        });
    
        try {
          const products = await sql`SELECT id, name, price FROM products LIMIT 10`;
          // Close in the background so it does not delay the response
          ctx.waitUntil(sql.end());
          return Response.json(products);
        } catch (err) {
          console.error(err);
          return Response.json({ error: String(err) }, { status: 500 });
        }
      }
    };
  5. Deploy

    Publish the Worker. Queries now flow through the pooled, cached fast lane.

    bash
    npx wrangler deploy

Reads and writes, safely

jsqueries.js
// Parameterized values are sent separately, never string-concatenated
const id = 42;
const rows = await sql`SELECT id, name FROM products WHERE id = ${id}`;

// Writes (INSERT/UPDATE/DELETE) are never cached — they always hit Postgres
await sql`INSERT INTO views (product_id) VALUES (${id})`;
key

Keep the real URL out of your code

The database password lives only inside the Hyperdrive config you created in step 1. Always connect via env.HYPERDRIVE.connectionString so the secret never ends up in your source or bundle.

school

Key concepts

link

Connection string

The URL with host, port, user, password, and DB name. Hyperdrive hides yours behind env.HYPERDRIVE.connectionString.

pool

Connection pool

A set of warm, ready connections shared across requests so nobody pays the setup cost twice.

cached

Query cache

Cacheable SELECT results are remembered (default 60s) so identical repeats skip the database.

hourglass_empty

Setup latency

The TCP + TLS + auth handshake a fresh connection must finish before any query runs. Pooling pays it once.

block

Connection limits

Every Postgres has a max connection count. Bursty serverless traffic can exhaust it — pooling keeps usage flat.

verified

DB stays the source

Hyperdrive only accelerates access. Your real rows always live in your own Postgres.

tips_and_updates

Tips & gotchas

tune

Tune caching for freshness

Caching is on by default with a 60s max age (configurable up to 1 hour). For data that must always be live, disable it with: npx wrangler hyperdrive update <id> --caching-disabled true

  • Without compatibility_flags = ["nodejs_compat"] and a recent compatibility_date, the Postgres driver fails to load.
  • Only read-only SELECTs are cached; INSERT/UPDATE/DELETE always reach Postgres.
  • Queries using NOW(), RANDOM(), or CURRENT_DATE are treated as uncacheable — even just naming one in a SQL comment disables caching for that query.
  • Always close with ctx.waitUntil(sql.end()) so cleanup never delays the response.
  • Keep the per-isolate pool small (e.g. max: 5); Hyperdrive multiplexes across them anyway.
  • Works with Postgres and Postgres-compatibles (Neon, Supabase, RDS, CockroachDB, Timescale) — and MySQL too.
  • Need a Cloudflare-native SQL database instead of bringing your own? See D1.