Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions src/content/docs/hyperdrive/best-practices.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
---
title: Best practices
pcx_content_type: concept
description: Apply reliable pooling, caching, placement, and connection patterns.
sidebar:
order: 3.5
products:
- hyperdrive
---

import { TypeScriptExample } from "~/components";

Use [Hyperdrive](/hyperdrive/) as the standard connection path when a [Worker](/workers/) accesses a remote PostgreSQL or MySQL database.

Hyperdrive performs connection setup at the edge and pools connections near your database. It can also cache eligible read queries.

Refer to [How Hyperdrive works](/hyperdrive/concepts/how-hyperdrive-works/) for the full request path.

## Choose an architecture

### Separate cached and cache-disabled reads

If your application has different freshness needs, create two Hyperdrive configurations. Use a cached configuration by default and a cache-disabled configuration for fresh reads.

Use the cache-disabled configuration for authentication, sessions, permissions, billing state, and read-after-write operations. Both configurations still provide connection pooling and fast connection setup.

Create the second configuration with caching disabled. Then [bind both configurations](/hyperdrive/concepts/query-caching/#disable-caching) to your Worker.

Create clients lazily for the bindings each request needs.

Each configuration has an independent origin connection pool. Count their combined capacity against the database connection limit.

### Split distinct traffic policies

Use separate configurations when traffic needs distinct freshness policies. You can also target a read-replica endpoint for read-only traffic.

Hyperdrive does not automatically route queries to replicas. Configure the replica endpoint and any provider-specific replica credentials yourself.

Include every configuration when calculating origin capacity. Refer to [Query caching](/hyperdrive/concepts/query-caching/#read-after-write-behavior) for the cached and fresh pattern.

## Manage client lifecycle

### Create clients inside handlers

Create database clients inside each Worker handler. Never create clients or driver-level pools in global scope.

Hyperdrive owns the origin connection pool. Routine calls to `client.end()` or an equivalent method are unnecessary.

This handler creates one node-postgres client and runs a parameterized query. It assumes you completed the [node-postgres setup](/hyperdrive/examples/connect-to-postgres/postgres-drivers-and-libraries/node-postgres/).

<TypeScriptExample filename="src/index.ts">

```ts
import { Client } from "pg";

export default {
async fetch(request: Request, env: Env): Promise<Response> {
const productId = new URL(request.url).searchParams.get("product_id");
if (!productId) {
return new Response("Missing product_id", { status: 400 });
}

const client = new Client({
connectionString: env.HYPERDRIVE.connectionString,
});

try {
await client.connect();
const result = await client.query<{ id: string; name: string }>(
"SELECT id, name FROM products WHERE id = $1",
[productId],
);

return Response.json(result.rows);
} catch (error) {
console.error("Database query failed", error);
return Response.json({ error: "Database error" }, { status: 500 });
}
},
} satisfies ExportedHandler<Env>;
```

</TypeScriptExample>

For lifecycle details, refer to [Connection lifecycle](/hyperdrive/concepts/connection-lifecycle/).

### Create clients within each runtime unit

In Durable Objects, generally create a client for each request or method call. Persistent clients consume Hyperdrive pool capacity while they remain open.

In Workflows, create the client inside each `step.do()`. Run every query from that client within the same step.

Refer to [Durable Object connection lifecycle](/hyperdrive/concepts/connection-lifecycle/#durable-objects-and-persistent-connections) and [Rules of Workflows](/workflows/build/rules-of-workflows/).

## Size connection pools

### Size pools conservatively

Start with a low origin connection limit. Increase it only when metrics show sustained contention.

Set the limit below the database maximum. The Hyperdrive limit is soft and can be exceeded temporarily.

Add the limits for every independent configuration using the database. Monitor waiting clients and open connections before increasing capacity.

Compare connection and query latency with your database metrics.

Use [Hyperdrive metrics](/hyperdrive/observability/metrics/), [pool tuning guidance](/hyperdrive/configuration/tune-connection-pool/), and [published limits](/hyperdrive/platform/limits/) when sizing pools.

### Connect to a direct endpoint

Point Hyperdrive at a direct database endpoint. Do not place PgBouncer or another transaction pooler between Hyperdrive and the database.

Hyperdrive already uses transaction pooling. Chaining poolers adds another pooling layer and complicates connection behavior.

### Keep transactions short

Use transactions only when several statements require atomicity. Keep them short because each transaction holds an origin connection.

Do not perform network requests or heavy computation inside transactions. Complete that work before opening the transaction.

A session-level `SET` does not persist after pool return. Apply settings within query or transaction scope, and account for the connection cost.

Refer to [Pooling mode](/hyperdrive/concepts/connection-pooling/#pooling-mode) and [Connection lifecycle](/hyperdrive/concepts/connection-lifecycle/#long-running-transactions).

## Cache queries safely

### Treat caching as best effort

Your database must handle cache misses and early evictions. Cached entries can disappear before their configured expiration.

Cache settings apply to each Hyperdrive configuration. Refer to [Query caching](/hyperdrive/concepts/query-caching/#default-cache-settings) for current settings and constraints.

Never cache authorization, session, or permission reads. Use the cache-disabled configuration whenever correctness requires fresh data.

Writes do not invalidate cached reads. Send read-after-write queries through the cache-disabled configuration.

### Isolate tenant-specific reads

Every tenant-specific query must include tenant or user identifiers as bound parameters. Enforce tenant scope in SQL or database policies as well.

Do not rely on session state or row-level security alone to separate cache entries.

A cache-disabled configuration does not replace tenant isolation. Do not depend on application filtering after a cached query.

If you cannot confirm cache safety, also route the query through the cache-disabled configuration.

### Preserve query cacheability

PostgreSQL `STABLE` and `VOLATILE` functions make queries uncacheable. SQL comments containing uncacheable function names can have the same effect.

Refer to [cacheable queries](/hyperdrive/concepts/query-caching/#what-does-hyperdrive-cache) before changing query text. Do not use SQL comments as cache controls.

Postgres.js must keep `prepare: true` for cacheable queries. Refer to the [Postgres.js example](/hyperdrive/examples/connect-to-postgres/postgres-drivers-and-libraries/postgres-js/) and [uncached query troubleshooting](/hyperdrive/observability/troubleshooting/#uncached-queries).

## Choose Worker placement

### Place Workers near the main dependency

Use [Workers Placement](/workers/configuration/placement/) when multiple sequential queries dominate request time. Placement is not automatically useful for one database query.

Choose a region or host near the main latency dependency. Measure total request latency before and after changing placement.

Refer to the [Hyperdrive placement guidance](/hyperdrive/reference/faq/#should-i-use-placement-with-hyperdrive) for selection criteria.

## Choose a network path

### Use a supported network path

A public database must provide a publicly reachable endpoint. Its firewall must allow the [Cloudflare IP ranges](/hyperdrive/configuration/firewall-and-networking-configuration/).

For private databases, use the recommended [Workers VPC connection](/hyperdrive/configuration/connect-to-private-database-vpc/) (Beta). Do not expose a private database only to satisfy Hyperdrive connectivity.

Hyperdrive requires Transport Layer Security (TLS) to the database. Review the [supported TLS modes](/hyperdrive/reference/supported-databases-and-features/#supported-tls-ssl-modes).

Hyperdrive supports PostgreSQL and MySQL wire-compatible databases. Review the [unsupported PostgreSQL features](/hyperdrive/reference/supported-databases-and-features/#unsupported-postgresql-features).

Before writing MySQL queries, review the [unsupported MySQL features](/hyperdrive/reference/supported-databases-and-features/#unsupported-mysql-features).

### Configure MySQL clients carefully

Hyperdrive does not support MySQL prepared statements or multi-statement queries. Use parameterized text queries that your driver does not prepare.

With `mysql2`, use the documented version and set `disableEval: true`. Refer to the [`mysql2` example](/hyperdrive/examples/connect-to-mysql/mysql-drivers-and-libraries/mysql2/) for setup details.

### Query through runtime bindings

Use Hyperdrive bindings from Workers runtime contexts to query databases. The [Hyperdrive REST API](/hyperdrive/hyperdrive-rest-api/) manages configurations and does not accept database queries.

## Handle failures

### Retry only transient failures

Retry only failures that your application knows are transient. Keep every retry policy bounded.

Before retrying a write, make the operation idempotent. Do not retry every database error.

Refer to [Troubleshoot and debug](/hyperdrive/observability/troubleshooting/) for known error conditions.

### Follow published limits

Design against the current [Hyperdrive limits](/hyperdrive/platform/limits/). Do not copy limit values into application assumptions because they can change.

## Related resources

- [How Hyperdrive works](/hyperdrive/concepts/how-hyperdrive-works/)
- [Connection lifecycle](/hyperdrive/concepts/connection-lifecycle/)
- [Query caching](/hyperdrive/concepts/query-caching/)
- [Metrics and analytics](/hyperdrive/observability/metrics/)
- [Supported databases and features](/hyperdrive/reference/supported-databases-and-features/)