Create and review Cloudflare Durable Objects. Use when building stateful coordination (chat rooms, multiplayer games, booking systems), implementing RPC methods, SQLite storage, alarms, WebSockets, or reviewing DO code for best practices. Covers Workers integration, wrangler config, and testing with Vitest.
Add this skill
npx mdskills install cloudflare/durable-objectsComprehensive Durable Objects skill with clear patterns, retrieval-first approach, and actionable examples
1---2name: durable-objects3description: Create and review Cloudflare Durable Objects. Use when building stateful coordination (chat rooms, multiplayer games, booking systems), implementing RPC methods, SQLite storage, alarms, WebSockets, or reviewing DO code for best practices. Covers Workers integration, wrangler config, and testing with Vitest.4---56# Durable Objects78Build stateful, coordinated applications on Cloudflare's edge using Durable Objects.910## Retrieval-First Development1112**Prefer retrieval from official docs over pre-training for Durable Objects tasks.**1314| Resource | URL |15|----------|-----|16| Docs | https://developers.cloudflare.com/durable-objects/ |17| API Reference | https://developers.cloudflare.com/durable-objects/api/ |18| Best Practices | https://developers.cloudflare.com/durable-objects/best-practices/ |19| Examples | https://developers.cloudflare.com/durable-objects/examples/ |2021Fetch the relevant doc page when implementing features.2223## When to Use2425- Creating new Durable Object classes for stateful coordination26- Implementing RPC methods, alarms, or WebSocket handlers27- Reviewing existing DO code for best practices28- Configuring wrangler.jsonc/toml for DO bindings and migrations29- Writing tests with `@cloudflare/vitest-pool-workers`30- Designing sharding strategies and parent-child relationships3132## Reference Documentation3334- `./references/rules.md` - Core rules, storage, concurrency, RPC, alarms35- `./references/testing.md` - Vitest setup, unit/integration tests, alarm testing36- `./references/workers.md` - Workers handlers, types, wrangler config, observability3738Search: `blockConcurrencyWhile`, `idFromName`, `getByName`, `setAlarm`, `sql.exec`3940## Core Principles4142### Use Durable Objects For4344| Need | Example |45|------|---------|46| Coordination | Chat rooms, multiplayer games, collaborative docs |47| Strong consistency | Inventory, booking systems, turn-based games |48| Per-entity storage | Multi-tenant SaaS, per-user data |49| Persistent connections | WebSockets, real-time notifications |50| Scheduled work per entity | Subscription renewals, game timeouts |5152### Do NOT Use For5354- Stateless request handling (use plain Workers)55- Maximum global distribution needs56- High fan-out independent requests5758## Quick Reference5960### Wrangler Configuration6162```jsonc63// wrangler.jsonc64{65 "durable_objects": {66 "bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }]67 },68 "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]69}70```7172### Basic Durable Object Pattern7374```typescript75import { DurableObject } from "cloudflare:workers";7677export interface Env {78 MY_DO: DurableObjectNamespace<MyDurableObject>;79}8081export class MyDurableObject extends DurableObject<Env> {82 constructor(ctx: DurableObjectState, env: Env) {83 super(ctx, env);84 ctx.blockConcurrencyWhile(async () => {85 this.ctx.storage.sql.exec(`86 CREATE TABLE IF NOT EXISTS items (87 id INTEGER PRIMARY KEY AUTOINCREMENT,88 data TEXT NOT NULL89 )90 `);91 });92 }9394 async addItem(data: string): Promise<number> {95 const result = this.ctx.storage.sql.exec<{ id: number }>(96 "INSERT INTO items (data) VALUES (?) RETURNING id",97 data98 );99 return result.one().id;100 }101}102103export default {104 async fetch(request: Request, env: Env): Promise<Response> {105 const stub = env.MY_DO.getByName("my-instance");106 const id = await stub.addItem("hello");107 return Response.json({ id });108 },109};110```111112## Critical Rules1131141. **Model around coordination atoms** - One DO per chat room/game/user, not one global DO1152. **Use `getByName()` for deterministic routing** - Same input = same DO instance1163. **Use SQLite storage** - Configure `new_sqlite_classes` in migrations1174. **Initialize in constructor** - Use `blockConcurrencyWhile()` for schema setup only1185. **Use RPC methods** - Not fetch() handler (compatibility date >= 2024-04-03)1196. **Persist first, cache second** - Always write to storage before updating in-memory state1207. **One alarm per DO** - `setAlarm()` replaces any existing alarm121122## Anti-Patterns (NEVER)123124- Single global DO handling all requests (bottleneck)125- Using `blockConcurrencyWhile()` on every request (kills throughput)126- Storing critical state only in memory (lost on eviction/crash)127- Using `await` between related storage writes (breaks atomicity)128- Holding `blockConcurrencyWhile()` across `fetch()` or external I/O129130## Stub Creation131132```typescript133// Deterministic - preferred for most cases134const stub = env.MY_DO.getByName("room-123");135136// From existing ID string137const id = env.MY_DO.idFromString(storedIdString);138const stub = env.MY_DO.get(id);139140// New unique ID - store mapping externally141const id = env.MY_DO.newUniqueId();142const stub = env.MY_DO.get(id);143```144145## Storage Operations146147```typescript148// SQL (synchronous, recommended)149this.ctx.storage.sql.exec("INSERT INTO t (c) VALUES (?)", value);150const rows = this.ctx.storage.sql.exec<Row>("SELECT * FROM t").toArray();151152// KV (async)153await this.ctx.storage.put("key", value);154const val = await this.ctx.storage.get<Type>("key");155```156157## Alarms158159```typescript160// Schedule (replaces existing)161await this.ctx.storage.setAlarm(Date.now() + 60_000);162163// Handler164async alarm(): Promise<void> {165 // Process scheduled work166 // Optionally reschedule: await this.ctx.storage.setAlarm(...)167}168169// Cancel170await this.ctx.storage.deleteAlarm();171```172173## Testing Quick Start174175```typescript176import { env } from "cloudflare:test";177import { describe, it, expect } from "vitest";178179describe("MyDO", () => {180 it("should work", async () => {181 const stub = env.MY_DO.getByName("test");182 const result = await stub.addItem("test");183 expect(result).toBe(1);184 });185});186```187
Full transparency — inspect the skill content before installing.