Connect Workers to PostgreSQL/MySQL with Hyperdrive's global pooling and caching. Use when: connecting to existing databases, setting up connection pools, using node-postgres/mysql2, integrating Drizzle/Prisma, or troubleshooting pool acquisition failures, TLS errors, or nodejs_compat missing. Prevents 11 documented errors.
Status: Production Ready ✅ Last Updated: 2026-01-09 Dependencies: cloudflare-worker-base (recommended for Worker setup) Latest Versions: [email protected], [email protected]+ (minimum), [email protected], [email protected]
Recent Updates (2025):
# For PostgreSQL
npx wrangler hyperdrive create my-postgres-db \
--connection-string="postgres://user:[email protected]:5432/database"
# For MySQL
npx wrangler hyperdrive create my-mysql-db \
--connection-string="mysql://user:[email protected]:3306/database"
# Output:
# ✅ Successfully created Hyperdrive configuration
#
# [[hyperdrive]]
# binding = "HYPERDRIVE"
# id = "a76a99bc-7901-48c9-9c15-c4b11b559606"
Save the id value - you'll need it in the next step!
Add to your wrangler.jsonc:
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2024-09-23",
"compatibility_flags": ["nodejs_compat"], // REQUIRED for database drivers
"hyperdrive": [
{
"binding": "HYPERDRIVE", // Available as env.HYPERDRIVE
"id": "a76a99bc-7901-48c9-9c15-c4b11b559606" // From wrangler hyperdrive create
}
]
}
CRITICAL:
nodejs_compat flag is REQUIRED for all database driversbinding is how you access Hyperdrive in code (env.HYPERDRIVE)id is the Hyperdrive configuration ID (NOT your database ID)# For PostgreSQL (choose one)
npm install pg # node-postgres (most common)
npm install postgres # postgres.js (modern, minimum v3.4.5)
# For MySQL
npm install mysql2 # mysql2 (minimum v3.13.0)
PostgreSQL with node-postgres (pg):
import { Client } from "pg";
type Bindings = {
HYPERDRIVE: Hyperdrive;
};
export default {
async fetch(request: Request, env: Bindings, ctx: ExecutionContext) {
const client = new Client({
connectionString: env.HYPERDRIVE.connectionString
});
await client.connect();
try {
const result = await client.query('SELECT * FROM users LIMIT 10');
return Response.json({ users: result.rows });
} finally {
// Clean up connection AFTER response is sent
ctx.waitUntil(client.end());
}
}
};
MySQL with mysql2:
import { createConnection } from "mysql2/promise";
export default {
async fetch(request: Request, env: Bindings, ctx: ExecutionContext) {
const connection = await createConnection({
host: env.HYPERDRIVE.host,
user: env.HYPERDRIVE.user,
password: env.HYPERDRIVE.password,
database: env.HYPERDRIVE.database,
port: env.HYPERDRIVE.port,
disableEval: true // REQUIRED for Workers (eval() not supported)
});
try {
const [rows] = await connection.query('SELECT * FROM users LIMIT 10');
return Response.json({ users: rows });
} finally {
ctx.waitUntil(connection.end());
}
}
};
npx wrangler deploy
That's it! Your Worker now connects to your existing database via Hyperdrive with:
This skill prevents 11 documented issues with sources and solutions.
Error: Connection fails with hostname like xxx.hyperdrive.local
Source: GitHub Issue #11556
Platforms: Windows, macOS 26 Tahoe, Ubuntu 24.04 LTS ([email protected]+)
Why It Happens: Hyperdrive local proxy hostname fails to resolve on certain platforms
Prevention:
Use environment variable for local development:
export CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE="postgres://user:password@localhost:5432/db"
npx wrangler dev
Or use wrangler dev --remote (caution: uses production database)
Status: Open issue, workaround available
Error: Connection hangs indefinitely with no error message Source: GitHub Issue #6179 Why It Happens: Using IP address instead of hostname in connection string causes postgres.js to hang Prevention:
// ❌ WRONG - IP address causes indefinite hang
const connection = "postgres://user:[email protected]:5432/db"
// ✅ CORRECT - Use hostname
const connection = "postgres://user:[email protected]:5432/db"
Additional Gotcha: Miniflare (local dev) only supports A-z0-9 characters in passwords, despite Postgres allowing special characters. Use simple passwords for local development.
Error: "unsupported authentication method" Source: GitHub Issue #10617 Why It Happens: MySQL 8.0.43+ introduces new authentication method not supported by Hyperdrive Prevention:
Use MySQL 8.0.40 or earlier, or configure user to use supported auth plugin:
ALTER USER 'username'@'%' IDENTIFIED WITH caching_sha2_password BY 'password';
Supported Auth Plugins: Only caching_sha2_password and mysql_native_password
Status: Known issue tracked as CFSQL-1392
Error: SSL required but connection fails in local development Source: GitHub Issue #10124 Why It Happens: Hyperdrive local mode doesn't support SSL connections to remote databases (e.g., Neon, cloud providers) Prevention:
Use conditional connection in code:
const url = env.isLocal ? env.DB_URL : env.HYPERDRIVE.connectionString;
const client = postgres(url, {
fetch_types: false,
max: 2,
});
Alternative: Use wrangler dev --remote (⚠️ connects to production database)
Timeline: SSL support planned for 2026 (requires workerd/Workers runtime changes, tracked as SQC-645)
Error: SET statements don't persist across queries Source: Cloudflare Hyperdrive Docs - How Hyperdrive Works Why It Happens: Hyperdrive operates in transaction mode where connections are returned to pool after each transaction and RESET Prevention:
// ❌ WRONG - SET won't persist across queries
await client.query('SET search_path TO myschema');
await client.query('SELECT * FROM mytable'); // Uses default search_path!
// ✅ CORRECT - SET within transaction
await client.query('BEGIN');
await client.query('SET search_path TO myschema');
await client.query('SELECT * FROM mytable'); // Now uses myschema
await client.query('COMMIT');
⚠️ WARNING: Wrapping multiple operations in a single transaction to maintain SET state will affect Hyperdrive's performance and scaling.
Error: Worker hangs and times out after first request Source: GitHub Issue #28193 Verified: Multiple users confirmed Why It Happens: Prisma's connection pool attempts to reuse connections across request contexts, violating Workers' I/O isolation Prevention:
// ❌ WRONG - Global Prisma client reused across requests
const prisma = new PrismaClient({ adapter });
export default {
async fetch(request: Request, env: Bindings) {
// First request: works
// Subsequent requests: hang indefinitely
const users = await prisma.user.findMany();
return Response.json({ users });
}
};
// ✅ CORRECT - Create new client per request
export default {
async fetch(request: Request, env: Bindings, ctx: ExecutionContext) {
const pool = new Pool({
connectionString: env.HYPERDRIVE.connectionString,
max: 5
});
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
try {
const users = await prisma.user.findMany();
return Response.json({ users });
} finally {
ctx.waitUntil(pool.end());
}
}
};
Error: Hyperdrive provides no benefit with Neon serverless driver Source: Neon GitHub Repo, Cloudflare Docs Why It Happens: Neon's serverless driver uses WebSockets instead of TCP, bypassing Hyperdrive's connection pooling Prevention:
// ❌ WRONG - Neon serverless driver bypasses Hyperdrive
import { neon } from '@neondatabase/serverless';
const sql = neon(env.HYPERDRIVE.connectionString);
// This uses WebSockets, not TCP - Hyperdrive doesn't help
// ✅ CORRECT - Use traditional TCP driver with Hyperdrive
import postgres from 'postgres';
const sql = postgres(env.HYPERDRIVE.connectionString, {
prepare: true,
max: 5
});
Official Recommendation: Neon documentation states "On Cloudflare Workers, consider using Cloudflare Hyperdrive instead of this driver"
Error: Double-pooling causes connection issues Source: Cloudflare Docs - Supabase Why It Happens: Using Supabase pooled connection (Supavisor) creates double-pooling; Supavisor doesn't support prepared statements Prevention:
# ❌ WRONG - Using Supabase pooled connection (Supavisor)
npx wrangler hyperdrive create my-supabase \
--connection-string="postgres://user:[email protected]:6543/postgres"
# ✅ CORRECT - Use Supabase direct connection
npx wrangler hyperdrive create my-supabase \
--connection-string="postgres://user:[email protected]:5432/postgres"
Reason: Hyperdrive provides its own pooling; double-pooling causes issues and breaks caching
Error: 500 errors approximately 95% of the time
Source: GitHub Issue #3893
Verified: Reproduced by multiple users
Why It Happens: Nitro 3's built-in useDatabase (db0/integrations/drizzle) has I/O isolation issues
Prevention:
// ❌ WRONG - Nitro's useDatabase fails ~95% of the time
import { useDatabase } from 'db0';
import { drizzle } from 'db0/integrations/drizzle';
export default eventHandler(async () => {
const db = useDatabase();
const users = await drizzle(db).select().from(usersTable);
// Fails ~95% of the time with 500 error
});
// ✅ CORRECT - Create Drizzle client directly
import postgres from 'postgres';
import { drizzle } from 'drizzle-orm/postgres-js';
export default eventHandler(async (event) => {
const sql = postgres(event.context.cloudflare.env.HYPERDRIVE.connectionString, {
max: 5,
prepare: true
});
const db = drizzle(sql);
const users = await db.select().from(usersTable);
event.context.cloudflare.ctx.waitUntil(sql.end());
return { users };
});
Error Message: "Cannot perform I/O on behalf of a different request"
Error: Prepared statement caching doesn't work properly Source: Cloudflare Docs Why It Happens: postgres.js versions before 3.4.5 don't support Hyperdrive's prepared statement caching properly Prevention:
# Minimum version for Hyperdrive compatibility
npm install [email protected]
# Current recommended version
npm install [email protected]
Related: May 2025 prepared statement caching improvements require minimum version 3.4.5
Error: Hyperdrive provides no benefit or causes connection issues Source: General pattern from Neon serverless driver issue Why It Happens: Hyperdrive requires TCP connections; WebSocket-based drivers bypass the TCP pooling layer Prevention:
Use traditional TCP-based drivers (pg, postgres.js, mysql2) instead of WebSocket-based drivers.
Affected Drivers: Any database driver using WebSockets instead of TCP
Hyperdrive eliminates 7 connection round trips (TCP + TLS + auth) by:
Result: Single-region databases feel globally distributed.
# PostgreSQL