This site is not affiliated with or endorsed by Cloudflare, Inc. It showcases deployable experiments across most Cloudflare products.

Blog21 min read

Stateful APIs at the edge with Durable Objects

Deep Durable Objects guide on Cloudflare - IDs, storage, SQLite, alarms, WebSockets, when not to use DOs, and experiments from counter to chat.

Teal LED panel glowing in a dark technology setting
Photo by Adi Goldstein on Unsplash

Stateless Workers scale easily, but some features need strongly consistent state: counters, presence, turn-based games, per-document locks, or chat rooms. Durable Objects give you a single-threaded instance addressed by ID - the right primitive when “last write wins in KV” is not enough.

Start with the Durable Counter experiment, then graduate to richer DO apps in the catalog.

Cloudflare products in this pattern

ProductRoleDocs
Durable ObjectsPer-ID strongly consistent compute + storageProduct home
DO storage APIKey-value transactional storageStorage
SQLite in Durable ObjectsRelational state per objectSQLite
DO alarmsDeferred work tied to an objectAlarms
WorkersStub creation + HTTP/WebSocket entryRuntime
WebSocketsRealtime fanout via DOWebSockets
QueuesOffload heavy work from the DOAsync jobs

When to reach for Durable Objects

Use a DO when you need one logical entity with serialized access:

  • Per-user or per-room state
  • Coordinating multiple clients over WebSockets
  • Alarms / deferred work tied to that entity
  • SQLite storage colocated with the object (DO SQLite Notes)

Prefer KV, D1, or R2 when entities are independent and eventual consistency is fine. Prefer Queues / Workflows when the problem is async fan-out rather than a sticky entity.

Official overview: What are Durable Objects?

Mental model

Client → Worker stub → idFromName("room-42") → Durable Object instance

All requests for the same ID hit the same instance. That is the feature: no distributed lock service for the common case.

Read:

The counter teaches:

  1. Exporting a DO class from the Worker
  2. Binding it in wrangler.json
  3. Getting a stub by name/id
  4. Reading/writing storage transactionally via the storage API

From counter to real products

PatternExperimentCloudflare docs angle
Increment / read / resetDurable CounterStorage API
Chat + optional Workers AIChat AgentDO + Workers AI
Notes in DO SQLiteDO SQLite NotesSQLite storage
WebSocket echoWebSocket EchoWebSockets
Alarms / scheduled DO workDO Alarm SchedulerAlarms
Queue jobs + DO viewQueue Job VisualizerDO + Queues

Copy one pattern at a time. A chat app that also invents a novel storage layer on day one is how experiments become unmaintainable.

Design tips from the product docs

  • Name IDs carefully - idFromName is a permanent partition key for that name (ID docs)
  • Keep handlers small - do heavy AI/fetch work in the Worker or a Queue; coordinate in the DO
  • Use storage transactions for multi-key updates (storage API)
  • Alarms for deferred cleanup or digests instead of external cron when state-local (alarms)
  • Hibernation - understand WebSocket hibernation if you build long-lived realtime (WebSockets + hibernation)

Anti-patterns

  • One global DO for all users (hotspot)
  • Storing large blobs in DO storage instead of R2
  • Running multi-minute AI jobs inside the DO request path
  • Using DOs when a simple KV counter with lossy increments would do

Deploy the counter first

  1. Read Durable Objects get started
  2. Open Durable Counter
  3. Deploy and call increment a few times
  4. Confirm consistent reads for the same object id
  5. Open Chat Agent or DO SQLite Notes when you need a richer shape

You learn DO addressing and storage with the smallest useful API, then compose realtime or SQLite without rewriting the foundation - Workers as the vehicle, Durable Objects as the product capability.

Production checklist

  1. Design ID scheme first (idFromName is forever for that name).
  2. Keep DO handlers short; offload heavy work to Queues.
  3. Use transactions for multi-key updates (storage API).
  4. Prefer SQLite in DOs when you need relational queries per entity.
  5. Load-test hot IDs - one global DO is an anti-pattern.
  6. Plan alarms for cleanup (alarms).

FAQ

DO vs D1?

D1 is shared SQL across requests. A Durable Object is a sticky single-threaded entity. Use D1 for catalogs; DOs for rooms, sessions, and locks.

Can I put blobs in DO storage?

Small state yes; large files belong in R2.

Related experiments

These deployable references implement the patterns in this article. Open the docs for API details, then Deploy or clone the source.

  • Durable Counter - Canonical Durable Objects counter - globally consistent increment/decrement with strong consistency at the edge
  • Chat Agent - Durable AI chat agent with SQLite-backed Durable Objects and optional Workers AI - stateful conversational agent on Workers
  • DO SQLite Notes - Per-user notes in SQLite-backed Durable Objects via sql.exec - structured storage pattern for stateful Workers
  • WebSocket Echo - WebSocket echo server on Cloudflare Workers - bidirectional realtime messaging reference implementation
  • DO Alarm Scheduler - Schedule one-off reminders with the Durable Object Alarm API and poll fired status - deferred edge jobs without Cron
  • Queue Job Visualizer - Cloudflare Queues producer/consumer with KV-backed job status and simulated retries - visual job queue reference

Cloudflare Experiments is an independent open-source catalog - not affiliated with Cloudflare, Inc.