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
| Feature | Details |
|---|---|
| Consistency | Strongly consistent — single point of coordination |
| Persistence | Built-in transactional storage (key-value on the Object) |
| WebSocket support | ✅ Native — ideal for real-time apps |
| Location | Automatically colocated near the first caller |
| Use cases | Chat rooms, collaborative editing, rate limiters, game state, counters |
Free Tier
| Resource | Free Plan |
|---|---|
| Requests | 1 million per month |
| Duration | 400,000 GB-seconds per month |
| Storage read | 1 million per month |
| Storage write | 1 million per month |
| Storage delete | 1 million per month |
| Stored data | 1 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
| Need | Solution |
|---|---|
| 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.