惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
C
CERT Recently Published Vulnerability Notes
Forbes - Security
Forbes - Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
L
LINUX DO - 最新话题
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
SecWiki News
SecWiki News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The Last Watchdog
The Last Watchdog
S
Schneier on Security
T
Troy Hunt's Blog
N
News | PayPal Newsroom
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Schneier on Security
Schneier on Security
P
Privacy & Cybersecurity Law Blog
T
Tor Project blog
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
A
Arctic Wolf
S
Secure Thoughts
P
Proofpoint News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Security Latest
Security Latest
Scott Helme
Scott Helme
Security Archives - TechRepublic
Security Archives - TechRepublic
Latest news
Latest news
PCI Perspectives
PCI Perspectives
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
V2EX - 技术
V2EX - 技术
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
V
Vulnerabilities – Threatpost
T
Threat Research - Cisco Blogs
Webroot Blog
Webroot Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
News and Events Feed by Topic
TaoSecurity Blog
TaoSecurity Blog
大猫的无限游戏
大猫的无限游戏
T
Tenable Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
Visual Studio Blog
H
Hacker News: Front Page
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog

Master.dev Blog RSS Feed

Thinking Horizontally in CSS @layer How to Make an Interactive Element Invisible but Accessible Boundary-Aware Styling in CSS The Field Guide to Grid Lanes Totally Free Course: Claude Code Uber for Dogs: How to Stop & Think for Design Fluid Typography with progress() Introduction to Cloudflare Workers for Web Apps Modern Web Guidance Let’s Play With Gap Decorations!
Cloudflare Workers and Hyperdrive with TanStack Start
Adam Rackis on July 2, 2026 · 2026-07-03 · via Master.dev Blog RSS Feed

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.

Article Series

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({
  dialect: "postgresql",
  dbCredentials: {
    url: connectionString,
  },
  out: "./src/drizzle",
});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.

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";
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.POSTGRES!,
});

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";
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.POSTGRES!,
});

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

Cloudflare Error

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 button.

You’ll be greeted with a few options for how to proceed. For this post, I’ll be using PlanetScale.

Follow the prompts, authenticate if needed, select your database, and most importantly, be sure to fill in your database name; you almost certainly do not want the default value of the postgres.

Once complete, you should be greeted with a new Wrangler entry.

That’s what mine looks like, and no, there’s nothing secret or private about that data. In fact, you’ll need it in your Wrangler file and to have committed in Git if you want Cloudflare’s GitHub integration to work.

Copy that into your Wrangler file.

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "fitness-tracker",
  "compatibility_date": "2025-09-02",
  "compatibility_flags": ["nodejs_compat"],
  "main": "@tanstack/react-start/server-entry",
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "dd0103f82a11410b91c8fb5752050a21"
    }
  ],
  "observability": {
    "enabled": true
  },
  "upload_source_maps": true
}Code language: JSON / JSON with Comments (json)

Now update your typings by running npx wrangler types.

Connect to Hyperdrive

And now, via your same env object, you can connect to your database through Hyperdrive.

If you haven’t read Part 1 of this, you can grab the env object in a TanStack project with this special import 

import { env } from "cloudflare:workers";Code language: JavaScript (javascript)
const pool = new Pool({
  connectionString: env.HYPERDRIVE.connectionString,
});Code language: JavaScript (javascript)

But there’s one more thing to do. When you attempt to run your app, you’ll likely see this error

To fix this, add this connection string to your dev database with that key to your .env file.

CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE='postgresql://docker:docker@localhost:5432/your_db'

Fixing Per-Request Cleanup

Our database connections will be much snappier now. But Cloudflare will still error out since our database object is created and exported by a module. That means it will continue to live between requests.

Let’s see how to fix that.

What we need is a fresh database connection per request. And it turns out TanStack Start has a feature just for that: global request middleware

Note that I’m using the word “connection” loosely here. We’re creating a conceptual “connection” to our database per request, but we’re really connecting through Hyperdrive, which pools and reuses actual database TCP connections between requests. The goal is not to literally create a fresh TCP connection to our database for each request; it’s to avoid long-lived I/O resources between requests.

The focus of this post is Cloudflare, so we’ll breeze through the code; check the docs for more info.

<em>// src/start.ts</em>
import { Pool } from "pg";
import { createCsrfMiddleware, createMiddleware, createStart } from "@tanstack/react-start";
import { getDb } from "./data/db";

const globalContextMiddleware = createMiddleware().server(async ({ next }) => {
  try {
    const pool = new Pool({
      connectionString: process.env.POSTGRES!,
    });

    const db = getDb(pool);

    return next({
      context: {
        db,
      },
    });
  } catch (error) {
    console.log({ msg: "Error in root context middleware", error });
    throw error;
  }
});

const csrfMiddleware = createCsrfMiddleware({
  filter: ctx => ctx.handlerType === "serverFn",
});

export const startInstance = createStart(() => ({
  requestMiddleware: [csrfMiddleware, globalContextMiddleware],
  functionMiddleware: [],
}));Code language: TypeScript (typescript)

This middleware will run once per request, which is exactly what we want. We create our database object and then add it to the context.

return next({
  context: {
    db,
  },
});Code language: JavaScript (javascript)

Now you can access the db object from any server functions, or server routes (API routes) via the context object that’s passed in.

Odds and Ends

If you’re connecting to a database that’s hosted in a particular region, you’ll almost always want your web app served from the same region. Putting the workers serving your app closer to your users might seem appealing, but that only serves to make the workers further from your database, increasing the latency of your queries and updates, and your app will likely need to make multiple requests to your database in the process of serving a request.

My PlanetScale database is in AWS’s us-east-1 region, and so I can pin my Cloudflare app to the same region with this entry in my Wrangler file.

"placement": {
  "region": "aws:us-east-1",
},Code language: JSON / JSON with Comments (json)

It can make a large difference. I saw the latency in running a very simple query against a small table explode from about 7ms when placed in the same region as my database, up to over 10X (about 80ms) when placed on the United States West Coast.

Concluding Thoughts

I absolutely love Cloudflare’s development platform. Workers are an outstanding, low-latency way to host your web application. I hope this post has provided the tools you need to get your first app up and running there.

Article Series