Cloudflare Workers and Hyperdrive with TanStack Start – Master.dev Blog
/ BLOG
CloudflareCloudflare WorkersHyperdriveTanStack
Cloudflare Workers and Hyperdrive with TanStack Start
Adam Rackis<br>on<br>July 2, 2026
Welcome to Part 2 of our series on using Cloudflare for Web Apps (and specifically TanStack Start). In Part 1, we covered the absolute basics. We deployed a web app to Cloudflare, saw how our Wrangler file works, set up some secrets, and we saw Cloudflare generate typings to keep TypeScript happy.
Introduction to Cloudflare Workers for Web Apps
Cloudflare Workers and Hyperdrive with TanStack Start
Now, we’ll set up a database. We’ll look at Hyperdrive and why it’s needed, as well as some potentially counterintuitive ways we need to set up our database object (or any I/O object). We’ll use TanStack Start specifically here, but these principles apply to any web framework, though some implementation details might differ slightly.
Preliminaries
I love Drizzle and use it for all my projects. It’s an outstanding, unique ORM that’s essentially a thin TypeScript layer on top of SQL. I’ve written about it here and here.
I’ll be using Postgres, so we’ll also install some utilities.
npm i drizzle-orm@rc drizzle-kit@rc pg @types/pgCode language: Bash (bash)
Next, add a drizzle.config.ts file.
import { defineConfig } from "drizzle-kit";
const connectionString = process.env.POSTGRES!;
export default defineConfig({<br>dialect: "postgresql",<br>dbCredentials: {<br>url: connectionString,<br>},<br>out: "./src/drizzle",<br>});Code language: JavaScript (javascript)
Then run:
npx drizzle-kit pullCode language: Bash (bash)
That will generate our Drizzle schema. We won’t cover those specifics here. See the Drizzle posts above if you’re curious, but really you can query your data however you want; for the purposes of this post, it makes no difference which, if any, ORM you use.
TanStack Start & TanStack Query
Adam Rackis<br>Spotify
The Wrong Way (for Cloudflare)
For now, let’s do something fairly common that usually works well enough. We’ll add a db.ts module, with this code
import { drizzle } from "drizzle-orm/node-postgres";<br>import { Pool } from "pg";
const pool = new Pool({<br>connectionString: process.env.POSTGRES!,<br>});
export const db = drizzle({ client: pool });Code language: JavaScript (javascript)
Again, this has nothing to do with Drizzle. Export a connection to your database however you’d like. The issues will be the same.
Issue 1: Performance
Remember, Cloudflare workers spin up very quickly, on demand, as needed to serve requests. As these (potentially numerous) workers come into existence, each of them establishing a connection to your database poses two problems.
The first is performance. Opening a fresh database connection is a relatively slow operation. We don’t want that happening every time a worker spins up. This is not a concern limited to Cloudflare; any cloud function solution would have the same problem. A web application running atop AWS Lambda would not want to exacerbate existing cold starts by adding TCP database connection overhead, and, of course, low-latency Cloudflare workers would not want to introduce cold-start characteristics in this way.
The second is the sheer number of connections that would be stood up in this way. Again, this applies to any platform that works via cloud functions. As your app grows in traffic, the number of cloud functions (Cloudflare workers, AWS Lambda, etc) would grow to a large number, as would the number of connections open on your database. And databases always have a limit on the number of connections they support.
This is, of course, a solved problem. Solutions like PgBouncer pool pre-warm connections and act as a proxy to your database. Your application connects to PgBouncer, and PgBouncer provides an open connection. Cloudflare provides its own version of this called Hyperdrive, which we’ll look at shortly.
Issue 2: Per-Request Cleanup
Here’s this code again:
import { drizzle } from "drizzle-orm/node-postgres";<br>import { Pool } from "pg";
const pool = new Pool({<br>connectionString: process.env.POSTGRES!,<br>});
export const db = drizzle({ client: pool });Code language: JavaScript (javascript)
The second issue with this code is that it violates Cloudflare’s rule on what is allowed to persist between requests. Long-lived I/O resources such as Node.js connection pools do not fit the Workers execution model and can trigger runtime errors like this
Let’s solve both of these problems.
Hyperdrive
No matter how we create our database object in code, we don’t want to connect directly to our source database; we want to connect to a pre-warmed connection pool. Cloudflare provides one for us called Hyperdrive. To get started, go to the Cloudflare dashboard, and under Storage and databases, find the option for "Postgres & MySQL (Hyperdrive)".
Amusingly, the Hyperdrive in the menu option may be truncated with how they display it.
Hit the connect to database...