E

Elite Durable Objects Workshop

Boost productivity with intelligent stateful coordination with RPC, SQLite, and WebSockets. Built for Claude Code with best practices and real-world patterns.

SkillCommunitydevopsv1.0.0MIT
0 views0 copies

Durable Objects Workshop

Advanced Cloudflare Durable Objects development guide for building stateful edge applications including real-time collaboration, WebSocket servers, rate limiters, and distributed coordination systems.

When to Use This Skill

Choose Durable Objects when:

  • Building real-time collaborative features (shared documents, cursors)
  • Need WebSocket connections with persistent server-side state
  • Implementing distributed rate limiting or counters
  • Building game servers or chat systems at the edge
  • Need strong consistency guarantees for specific entities

Consider alternatives when:

  • Need simple caching — use Workers KV
  • Need relational queries — use D1
  • Need long-running background jobs — use Workers Queues
  • Need global read-heavy data — use KV (Durable Objects are single-location)

Quick Start

# Activate Durable Objects workshop claude skill activate elite-durable-objects-workshop # Build a collaborative feature claude "Build a real-time collaborative document editor using Durable Objects" # Implement rate limiting claude "Create a distributed rate limiter with Durable Objects"

Example: WebSocket Chat Room

// Durable Object class export class ChatRoom { private sessions: Map<WebSocket, { name: string }> = new Map(); private state: DurableObjectState; constructor(state: DurableObjectState) { this.state = state; } async fetch(request: Request): Promise<Response> { const url = new URL(request.url); if (url.pathname === '/websocket') { if (request.headers.get('Upgrade') !== 'websocket') { return new Response('Expected WebSocket', { status: 400 }); } const pair = new WebSocketPair(); const [client, server] = Object.values(pair); const name = url.searchParams.get('name') || 'Anonymous'; this.sessions.set(server, { name }); server.accept(); server.addEventListener('message', (event) => { const message = JSON.stringify({ sender: name, text: event.data, timestamp: Date.now(), }); // Broadcast to all connected clients for (const [ws] of this.sessions) { if (ws !== server && ws.readyState === WebSocket.READY_STATE_OPEN) { ws.send(message); } } }); server.addEventListener('close', () => { this.sessions.delete(server); this.broadcast({ type: 'leave', name }); }); // Notify others this.broadcast({ type: 'join', name }); return new Response(null, { status: 101, webSocket: client }); } // REST endpoint for room info if (url.pathname === '/info') { return Response.json({ connectedUsers: Array.from(this.sessions.values()).map(s => s.name), count: this.sessions.size, }); } return new Response('Not found', { status: 404 }); } private broadcast(data: object) { const message = JSON.stringify(data); for (const [ws] of this.sessions) { if (ws.readyState === WebSocket.READY_STATE_OPEN) { ws.send(message); } } } } // Worker that routes to Durable Objects export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); const roomId = url.searchParams.get('room') || 'default'; const id = env.CHAT_ROOM.idFromName(roomId); const room = env.CHAT_ROOM.get(id); return room.fetch(request); }, };

Core Concepts

Durable Object Characteristics

FeatureDescription
Single InstanceExactly one instance per ID, globally
In-Memory StateFast access to state between requests
Persistent StorageTransactional KV storage via state.storage
WebSocket SupportNative WebSocket handling with hibernation
AlarmsScheduled future execution per object
LocationRuns near the first client that accesses it

Use Case Patterns

PatternDescriptionExample
Collaboration HubCentral state for multi-user editingGoogle Docs-style real-time editing
Entity ActorOne DO per entity managing its lifecycleUser session, game room, IoT device
Rate LimiterPer-key rate limiting with precise countersAPI rate limiting per customer
Queue ProcessorOrdered processing of events per partitionEvent sourcing per aggregate
Cache CoordinatorConsistent cache invalidationMulti-region cache coherence
// Rate Limiter Durable Object export class RateLimiter { private state: DurableObjectState; constructor(state: DurableObjectState) { this.state = state; } async fetch(request: Request): Promise<Response> { const now = Date.now(); const windowMs = 60_000; // 1 minute window const maxRequests = 100; // Get current window data const windowKey = `window:${Math.floor(now / windowMs)}`; const count = (await this.state.storage.get<number>(windowKey)) || 0; if (count >= maxRequests) { return Response.json( { error: 'Rate limit exceeded', retryAfter: Math.ceil((windowMs - (now % windowMs)) / 1000) }, { status: 429, headers: { 'Retry-After': String(Math.ceil((windowMs - (now % windowMs)) / 1000)) } } ); } await this.state.storage.put(windowKey, count + 1); // Clean up old windows const oldKey = `window:${Math.floor(now / windowMs) - 2}`; await this.state.storage.delete(oldKey); return Response.json({ remaining: maxRequests - count - 1, limit: maxRequests, reset: Math.ceil((windowMs - (now % windowMs)) / 1000), }); } }

Configuration

ParameterDescriptionDefault
class_nameDurable Object class nameRequired
script_nameWorker script containing the DOCurrent worker
websocket_hibernationEnable WebSocket hibernation APItrue
storage_limitMax storage per object1GB
alarm_retryRetry failed alarmstrue

Best Practices

  1. Use WebSocket Hibernation API for connection-heavy applications — Standard WebSocket handling keeps the DO awake (and billing) while connections are idle. The Hibernation API suspends the DO between messages, dramatically reducing costs for chat/collaboration apps.

  2. Design for single-threaded execution — Each Durable Object processes one request at a time. Design your state management assuming no concurrency within a single DO. Use state.storage.transaction() for multi-key atomic operations.

  3. Use idFromName() for deterministic routing, newUniqueId() for isolationidFromName("user-123") always routes to the same DO instance, ideal for per-entity state. newUniqueId() creates isolated instances when you need guaranteed separation.

  4. Implement heartbeat and cleanup with Alarms — Use Durable Object Alarms to schedule cleanup of stale connections, expired data, and periodic state persistence. Alarms survive DO restarts and ensure cleanup happens even if no clients are connected.

  5. Keep Durable Object storage small and focused — Store only the state needed for coordination. Offload large data to R2 or D1 and keep only references in the DO. Large storage slows down DO startup when it loads state from disk.

Common Issues

Durable Object starts responding slowly under load. Each DO is single-threaded — requests queue up if processing is slow. Optimize handler code, batch storage operations, and consider sharding load across multiple DO instances using consistent hashing.

WebSocket connections drop unexpectedly. Implement client-side reconnection with exponential backoff. On the server, use the close and error events to clean up session state. If using Hibernation API, handle the webSocketClose event handler properly.

Storage transactions fail with conflicts. Durable Object storage transactions use optimistic concurrency. If two operations modify the same keys, one will fail. Retry failed transactions with exponential backoff, and design key schemas to minimize conflicts between concurrent requests.

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates