Skip to main content

Durable Objects

Learning Focus

By the end of this lesson you will understand how Durable Objects provide strongly consistent state at the edge and their free-tier limits.

What Are Durable Objects?

Durable Objects provide stateful compute on Cloudflare's edge. Unlike Workers (stateless) or KV (eventually consistent), Durable Objects guarantee that only one instance handles a given object at any time, providing strong consistency.

flowchart LR
W1["Worker A\n(US)"] -->|Request| DO["Durable Object\n'room:123'\n(Single Instance)"]
W2["Worker B\n(EU)"] -->|Request| DO
W3["Worker C\n(Asia)"] -->|Request| DO

DO -->|"Consistent state"| STORAGE["Durable Storage\n(Transactional)"]

style DO fill:#7c3aed,color:#fff,stroke:#6d28d9
style STORAGE fill:#16a34a,color:#fff,stroke:#15803d

Key Characteristics

FeatureDetails
ConsistencyStrongly consistent — single point of coordination
PersistenceBuilt-in transactional storage (key-value on the Object)
WebSocket support✅ Native — ideal for real-time apps
LocationAutomatically colocated near the first caller
Use casesChat rooms, collaborative editing, rate limiters, game state, counters

Free Tier

ResourceFree Plan
Requests1 million per month
Duration400,000 GB-seconds per month
Storage read1 million per month
Storage write1 million per month
Storage delete1 million per month
Stored data1 GB

Basic Example: Counter

src/counter.ts
export class Counter {
state: DurableObjectState;

constructor(state: DurableObjectState) {
this.state = state;
}

async fetch(request: Request): Promise<Response> {
let count = (await this.state.storage.get<number>("count")) || 0;

if (request.method === "POST") {
count++;
await this.state.storage.put("count", count);
}

return Response.json({ count });
}
}
src/index.ts
export { Counter } from "./counter";

export default {
async fetch(request: Request, env: Env): Promise<Response> {
const id = env.COUNTER.idFromName("global-counter");
const obj = env.COUNTER.get(id);
return obj.fetch(request);
},
};
wrangler.toml
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"

[[migrations]]
tag = "v1"
new_classes = ["Counter"]

When to Use Durable Objects

NeedSolution
Real-time chat / collaboration✅ Durable Objects (WebSocket + state)
Rate limiting with exact counts✅ Durable Objects
Distributed counters✅ Durable Objects
User sessions (simple lookup)❌ Use KV
Blog posts / CMS data❌ Use D1

Key Takeaways

  • Durable Objects provide strongly consistent, stateful compute at the edge.
  • Each object has a unique identity and is guaranteed to run in a single location.
  • Built-in transactional storage and WebSocket support.
  • Free tier: 1M requests/month, 1 GB storage.
  • Ideal for real-time collaboration, counters, and coordination.

What's Next

  • Continue to Queues to learn about message queuing for asynchronous processing.