Cloudflare
Workers and Pages reach your cell through Hyperdrive - CapyDB creates the config, binds it, and rewrites it when credentials rotate.
What it does
Connecting a CapyDB project to a Cloudflare Worker or Pages project does more than copy a connection string:
- creates a Hyperdrive configuration pointing at the cell's pooled endpoint, so Workers get Cloudflare's edge connection pool instead of opening a TCP connection per invocation
- attaches the
HYPERDRIVEbinding to your Worker (a new Worker version, deployed) or your Pages project's production and preview configs - pushes
DATABASE_URL(pooled) andDATABASE_URL_UNPOOLED(direct) as Worker secrets or Pages environment variables, for migrations,wrangler dev, and drivers that do not go through Hyperdrive - pushes
CAPYDB_KV_REST_URLwhen the project has a K/V store - the URL only. The K/V token is shown once when you create or rotate it and is never retrievable, so setCAPYDB_KV_REST_TOKENyourself - rewrites the Hyperdrive origin after credential rotation, restores, imports, and promotions - the new password reaches every Worker with no redeploy, because the credentials live in the Hyperdrive config rather than in your bundle
Why Hyperdrive
Workers are short-lived and globally distributed. A fresh Postgres connection per invocation costs a TLS handshake plus authentication round trips from wherever the request landed - which for a cell in Helsinki and a visitor in São Paulo is the dominant cost of the request.
Hyperdrive keeps warm pooled connections near your database and serves the Worker from Cloudflare's network, so a query is one round trip inside Cloudflare rather than a connection setup across the Atlantic. It also caches read queries when you leave caching on.
CapyDB points Hyperdrive at the pooled endpoint (:6432), not the direct one. The cell's own PgBouncer is what bounds how many server connections the Postgres process ever sees, and it stays the front door regardless of who is in front of it. The Hyperdrive origin connection limit is sized from your plan's connection budget rather than left at Cloudflare's default, which a small plan cannot absorb.
Setup
Create a Cloudflare API token (Cloudflare dashboard → Manage Account → API Tokens → Create Token → Custom token) with, on the account that owns the Worker:
| Permission | Level | Why |
|---|---|---|
| Account → Workers Scripts | Edit | push secrets, attach the binding |
| Account → Hyperdrive | Edit | create and update the configuration |
| Account → Pages | Edit | only for a Pages target |
Then, in Dashboard → project → Settings → Integrations → Cloudflare:
- Paste the token and your account ID (right-hand sidebar of any Cloudflare account page, or
npx wrangler whoami). - Pick Workers or Pages and enter the Worker script name or Pages project name.
- Leave Create a Hyperdrive config on unless you only want the connection strings.
- Leave Attach the binding now on to have CapyDB deploy a new Worker version carrying the binding; turn it off if you would rather commit the binding to
wrangler.jsoncyourself.
CapyDB validates the token against the account and the target resource before saving it (encrypted, never returned), then runs the sync as a job you can watch in the dashboard.
Using the binding
import postgres from 'postgres'
export default {
async fetch(request: Request, env: Env) {
const sql = postgres(env.HYPERDRIVE.connectionString)
const rows = await sql`select now()`
return Response.json(rows)
},
}{
"compatibility_flags": ["nodejs_compat"],
"hyperdrive": [{ "binding": "HYPERDRIVE", "id": "<from the dashboard>" }],
}Postgres drivers need Node.js compatibility, so nodejs_compat is required on both Workers and Pages Functions. The dashboard shows the exact snippet with your configuration ID once the sync has run - copy it into wrangler.jsonc so that a later wrangler deploy does not drop a binding it does not know about.
wrangler deploy uploads the bindings declared in your configuration file. If the binding only
exists because CapyDB attached it, your next deploy removes it. Commit the snippet.
The CLI prints the same fragment, so you never have to copy an ID by hand:
capydb integrations env --target wrangler{
"compatibility_flags": ["nodejs_compat"],
"hyperdrive": [{ "binding": "HYPERDRIVE", "id": "…" }]
}Local development
wrangler dev simulates a Hyperdrive binding against a direct connection, which it reads from an
environment variable named after the binding:
# .dev.vars: what your Worker code reads (DATABASE_URL, DATABASE_DIRECT_URL, DATABASE_POOL_URL)
capydb integrations env --target dotenv > .dev.varscapydb integrations env --target wrangler prints the matching
CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> line on stderr - export it, then run
npx wrangler dev.
What a re-sync actually does
Syncs run again after credential rotation, restores, imports, and promotions. Each one compares what it is about to push against what it pushed last time, and writes nothing when they match - every Worker secret write and binding patch mints a new Worker version, and a no-op deploy of your Worker is not a side effect you asked for.
When something has changed, the connection secrets are rewritten and the Hyperdrive binding is re-attached only if it is missing or points at a different configuration.
Preview databases
Cloudflare has no per-branch environment to scope a database to - a Pages preview deploy and production read the same variables - so this integration does not create a database per branch. When you want an isolated database per pull request on Cloudflare, use the GitHub Action, which creates the preview and hands you its connection string inside the workflow.
Disconnecting
Disconnecting removes CapyDB's stored token and stops the syncs. Like the Vercel and Netlify integrations, it deliberately leaves what it created in your account: the Hyperdrive configuration, the secrets, and the binding stay so a running Worker does not lose its database mid-flight. Delete the configuration in the Cloudflare dashboard when you are ready.