Durable Objects Are Made for Agents

bretthoerner1 pts0 comments

Durable Objects are Made for Agents<br>Calvin French-Owen / Writing<br>Calvin French-Owen<br>About<br>Writing<br>Bookshelf

Durable Objects are Made for Agents

JUL 22, 2026

For the last few months, I've been building almost exclusively with Cloudflare Durable Objects (DOs). They are a wonderful primitive for building products (particularly agents), and I wish more providers out there had a similar offering. There really isn't anything like them in the market.

As I was comparing notes with various friends, I realized that most people haven't really even heard of DOs, and don't understand why they are such a big deal. It's a shame because they really are a near-perfect fit for building agents on top of them.

I won't gloss over the fact that there are shortcomings with DOs, and I wanted to share some thoughts about where they shine as well as where they fall short.

Disclosure: I am long a small amount of NET and am very bullish on their prospects.

What are Durable Objects?

Durable Objects combine a few simple concepts...

A serverless V8 isolate for running your code. It spins up and down on demand. You write some form of code that is JS/WASM compatible and it runs within v8. You have access to a subset of Node APIs. Each instance is keyed by ID , so you write code, which then is invoked many times (a la Lambda) but with unique context. This is essentially the same thing a Cloudflare Worker is doing.

A paired SQLite instance per DO. You get the ability to read and write to durable storage, and not really have to worry that much about reading and writing from the database.

Request routing to a particular isolate. The system that Cloudflare orchestrates in the background is the request routing and spinning up and down of each isolate.

Rather than thinking about your code as a bunch of services which talk to a database, you start thinking about your service as a set of many 'objects' which are event driven by new requests or by timers you can set (called alarms). Each one gets an ID, and requests are routed accordingly to a single isolate by ID. To see what I mean, here's what a simplified version of a Chat Room looks like:

// src/index.ts<br>import { DurableObject } from "cloudflare:workers";

export interface Env {<br>CHAT_ROOMS: DurableObjectNamespace;

// The outer Worker routes each request to the right room.<br>export default {<br>async fetch(request: Request, env: Env) {<br>// Example: /room/general<br>const name = new URL(request.url).pathname.split("/").pop();<br>if (!name) return new Response("Not found", { status: 404 });

// Every request for the same name reaches the same object.<br>const room = env.CHAT_ROOMS.getByName(name);<br>return room.fetch(request);<br>},<br>} satisfies ExportedHandler;

Each ChatRoom instance then handles its own state: it accepts websockets and broadcasts every message to everyone connected to that room:

// One Durable Object instance is one chat room.<br>export class ChatRoom extends DurableObjectEnv> {<br>private sockets = new Set();

async fetch(_request: Request): Promise {<br>const { 0: client, 1: server } = new WebSocketPair();<br>server.accept();<br>this.sockets.add(server);

server.addEventListener("message", (event) => {<br>for (const socket of this.sockets) socket.send(event.data);<br>});<br>server.addEventListener("close", () => {<br>this.sockets.delete(server);<br>});

return new Response(null, { status: 101, webSocket: client });

Once you start thinking this way... you start wanting to make everything a DO.

Sticking with the chat metaphor... suppose you are spinning up a Slack clone (as is trendy to talk about these days). You might lay out your data model like this:

WorkspaceDO: name, slug, channels, members<br>ChannelDO: name, topic, is_private, messages

When a user loads Slack, they typically want some sort of notification feed on the workspace, and a realtime feed of a channel. Sending a message to a channel should be ordered. You typically don't query across channels (except for a search use case, which can hit a vector store). If you wanted to go nuts you could even have specific ThreadDOs, or UserDOs, or any sort of DB you don't need to JOIN against.

The great parts about DOs

Starting with the beginning of the dev lifecycle, an extremely underrated part of working with DOs is their local dev setup. Locally, you run your services using wrangler. It's a CLI that functions as a local env + all-in-one toolkit for working with Cloudflare.

I initially was a bit weirded out by this, but using wrangler for local dev is actually quite nice. It becomes really quick to test changes when your coding agent doesn't rely on Docker or Postgres. Because it's all running SQLite in prod, the production drift for connecting to a DB is fairly minimal.

DOs also run explicitly in a single-threaded manner. That means you have to worry less about locking and concurrency. You know that only one invocation of your DO is running against its storage at a time.

Durable Objects also come with native support for websockets. This is a godsend...

durable request objects room name server

Related Articles