Durable objects skill

Build, debug, or review Cloudflare Durable Objects code for persistent state and coordination.

by cloudflare·Apache-2.0 license·★ 2,976 Stars on the repo·GitHub ↗

Use now

Files of Durable objects

cloudflare/main1 file shown
SKILL.md
Show the full text181 lines

Durable Objects

Build stateful, coordinated applications on Cloudflare's edge using Durable Objects.

Retrieval Sources

Your knowledge of Durable Objects APIs and configuration may be outdated. Prefer retrieval over pre-training for any Durable Objects task.

Resource URL
Docs https://developers.cloudflare.com/durable-objects/index.md
API Reference https://developers.cloudflare.com/durable-objects/api/index.md
Best Practices https://developers.cloudflare.com/durable-objects/best-practices/index.md
Examples https://developers.cloudflare.com/durable-objects/examples/index.md
Roles and permissions https://developers.cloudflare.com/workers/authorization/durable-objects/index.md

Fetch the relevant doc page when implementing features.

When to Use

  • Creating new Durable Object classes for stateful coordination
  • Implementing RPC methods, alarms, or WebSocket handlers
  • Reviewing existing DO code for best practices
  • Configuring wrangler.jsonc/toml for DO bindings and migrations
  • Writing tests with Cloudflare’s Vitest integration
  • Designing sharding strategies and parent-child relationships

Reference Documentation

  • ./references/rules.md - Core rules, storage, concurrency, RPC, alarms
  • Testing reference - Current Vitest documentation, migration choices, and test selection
  • ./references/workers.md - Workers handlers, types, wrangler config, observability

Search: blockConcurrencyWhile, idFromName, getByName, setAlarm, sql.exec

Core Principles

Use Durable Objects For
Need Example
Coordination Chat rooms, multiplayer games, collaborative docs
Strong consistency Inventory, booking systems, turn-based games
Per-entity storage Multi-tenant SaaS, per-user data
Persistent connections WebSockets, real-time notifications
Scheduled work per entity Subscription renewals, game timeouts
Do NOT Use For
  • Stateless request handling (use plain Workers)
  • Maximum global distribution needs
  • High fan-out independent requests

Quick Reference

Wrangler Configuration
// wrangler.jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]
}
Basic Durable Object Pattern
import { DurableObject } from "cloudflare:workers";

export interface Env {
  MY_DO: DurableObjectNamespace<MyDurableObject>;
}

export class MyDurableObject extends DurableObject<Env> {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    ctx.blockConcurrencyWhile(async () => {
      this.ctx.storage.sql.exec(`
        CREATE TABLE IF NOT EXISTS items (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          data TEXT NOT NULL
        )
      `);
    });
  }

  async addItem(data: string): Promise<number> {
    const result = this.ctx.storage.sql.exec<{ id: number }>(
      "INSERT INTO items (data) VALUES (?) RETURNING id",
      data
    );
    return result.one().id;
  }
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const stub = env.MY_DO.getByName("my-instance");
    const id = await stub.addItem("hello");
    return Response.json({ id });
  },
};

Critical Rules

  1. Model around coordination atoms - One DO per chat room/game/user, not one global DO
  2. Use getByName() for deterministic routing - Same input = same DO instance
  3. Use SQLite storage - Configure new_sqlite_classes in migrations
  4. Initialize in constructor - Use blockConcurrencyWhile() for schema setup only
  5. Use RPC methods - Not fetch() handler (compatibility date >= 2024-04-03)
  6. Persist first, cache second - Always write to storage before updating in-memory state
  7. One alarm per DO - setAlarm() replaces any existing alarm

Authorization

Durable Objects do not have separate roles or permissions; access follows the Worker that implements them. Retrieve the current Durable Objects authorization guidance before granting observability or Data Studio access, and scope the Workers role to the intended Worker or Workers product.

Anti-Patterns (NEVER)

  • Single global DO handling all requests (bottleneck)
  • Using blockConcurrencyWhile() on every request (kills throughput)
  • Storing critical state only in memory (lost on eviction/crash)
  • Using await between related storage writes (breaks atomicity)
  • Holding blockConcurrencyWhile() across fetch() or external I/O

Stub Creation

// Deterministic - preferred for most cases
const stub = env.MY_DO.getByName("room-123");

// From existing ID string
const id = env.MY_DO.idFromString(storedIdString);
const stub = env.MY_DO.get(id);

// New unique ID - store mapping externally
const id = env.MY_DO.newUniqueId();
const stub = env.MY_DO.get(id);

Storage Operations

// SQL (synchronous, recommended)
this.ctx.storage.sql.exec("INSERT INTO t (c) VALUES (?)", value);
const rows = this.ctx.storage.sql.exec<Row>("SELECT * FROM t").toArray();

// KV (async)
await this.ctx.storage.put("key", value);
const val = await this.ctx.storage.get<Type>("key");

Alarms

// Schedule (replaces existing)
await this.ctx.storage.setAlarm(Date.now() + 60_000);

// Handler
async alarm(): Promise<void> {
  // Process scheduled work
  // Optionally reschedule: await this.ctx.storage.setAlarm(...)
}

// Cancel
await this.ctx.storage.deleteAlarm();

Testing

Read the testing reference before configuring a suite or writing Durable Object tests. It routes to current setup, APIs, and examples and identifies the behavior to cover.

1---
2name: durable-objects
3description: Build, debug, or review Cloudflare Durable Objects code for persistent state and coordination.
4---
5 
6# Durable Objects
7 
8Build stateful, coordinated applications on Cloudflare's edge using Durable Objects.
9 
10## Retrieval Sources
11 
12Your knowledge of Durable Objects APIs and configuration may be outdated. **Prefer retrieval over pre-training** for any Durable Objects task.
13 
14| Resource | URL |
15|----------|-----|
16| Docs | https://developers.cloudflare.com/durable-objects/index.md |
17| API Reference | https://developers.cloudflare.com/durable-objects/api/index.md |
18| Best Practices | https://developers.cloudflare.com/durable-objects/best-practices/index.md |
19| Examples | https://developers.cloudflare.com/durable-objects/examples/index.md |
20| Roles and permissions | https://developers.cloudflare.com/workers/authorization/durable-objects/index.md |
21 
22Fetch the relevant doc page when implementing features.
23 
24## When to Use
25 
26- Creating new Durable Object classes for stateful coordination
27- Implementing RPC methods, alarms, or WebSocket handlers
28- Reviewing existing DO code for best practices
29- Configuring wrangler.jsonc/toml for DO bindings and migrations
30- Writing tests with Cloudflare’s Vitest integration
31- Designing sharding strategies and parent-child relationships
32 
33## Reference Documentation
34 
35- `./references/rules.md` - Core rules, storage, concurrency, RPC, alarms
36- [Testing reference](./references/testing.md) - Current Vitest documentation, migration choices, and test selection
37- `./references/workers.md` - Workers handlers, types, wrangler config, observability
38 
39Search: `blockConcurrencyWhile`, `idFromName`, `getByName`, `setAlarm`, `sql.exec`
40 
41## Core Principles
42 
43### Use Durable Objects For
44 
45| Need | Example |
46|------|---------|
47| Coordination | Chat rooms, multiplayer games, collaborative docs |
48| Strong consistency | Inventory, booking systems, turn-based games |
49| Per-entity storage | Multi-tenant SaaS, per-user data |
50| Persistent connections | WebSockets, real-time notifications |
51| Scheduled work per entity | Subscription renewals, game timeouts |
52 
53### Do NOT Use For
54 
55- Stateless request handling (use plain Workers)
56- Maximum global distribution needs
57- High fan-out independent requests
58 
59## Quick Reference
60 
61### Wrangler Configuration
62 
63```jsonc
64// wrangler.jsonc
65{
66 "durable_objects": {
67 "bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }]
68 },
69 "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]
70}
71```
72 
73### Basic Durable Object Pattern
74 
75```typescript
76import { DurableObject } from "cloudflare:workers";
77 
78export interface Env {
79 MY_DO: DurableObjectNamespace<MyDurableObject>;
80}
81 
82export class MyDurableObject extends DurableObject<Env> {
83 constructor(ctx: DurableObjectState, env: Env) {
84 super(ctx, env);
85 ctx.blockConcurrencyWhile(async () => {
86 this.ctx.storage.sql.exec(`
87 CREATE TABLE IF NOT EXISTS items (
88 id INTEGER PRIMARY KEY AUTOINCREMENT,
89 data TEXT NOT NULL
90 )
91 `);
92 });
93 }
94 
95 async addItem(data: string): Promise<number> {
96 const result = this.ctx.storage.sql.exec<{ id: number }>(
97 "INSERT INTO items (data) VALUES (?) RETURNING id",
98 data
99 );
100 return result.one().id;
101 }
102}
103 
104export default {
105 async fetch(request: Request, env: Env): Promise<Response> {
106 const stub = env.MY_DO.getByName("my-instance");
107 const id = await stub.addItem("hello");
108 return Response.json({ id });
109 },
110};
111```
112 
113## Critical Rules
114 
1151. **Model around coordination atoms** - One DO per chat room/game/user, not one global DO
1162. **Use `getByName()` for deterministic routing** - Same input = same DO instance
1173. **Use SQLite storage** - Configure `new_sqlite_classes` in migrations
1184. **Initialize in constructor** - Use `blockConcurrencyWhile()` for schema setup only
1195. **Use RPC methods** - Not fetch() handler (compatibility date >= 2024-04-03)
1206. **Persist first, cache second** - Always write to storage before updating in-memory state
1217. **One alarm per DO** - `setAlarm()` replaces any existing alarm
122 
123## Authorization
124 
125Durable Objects do not have separate roles or permissions; access follows the Worker that implements them. Retrieve the current [Durable Objects authorization guidance](https://developers.cloudflare.com/workers/authorization/durable-objects/index.md) before granting observability or Data Studio access, and scope the Workers role to the intended Worker or Workers product.
126 
127## Anti-Patterns (NEVER)
128 
129- Single global DO handling all requests (bottleneck)
130- Using `blockConcurrencyWhile()` on every request (kills throughput)
131- Storing critical state only in memory (lost on eviction/crash)
132- Using `await` between related storage writes (breaks atomicity)
133- Holding `blockConcurrencyWhile()` across `fetch()` or external I/O
134 
135## Stub Creation
136 
137```typescript
138// Deterministic - preferred for most cases
139const stub = env.MY_DO.getByName("room-123");
140 
141// From existing ID string
142const id = env.MY_DO.idFromString(storedIdString);
143const stub = env.MY_DO.get(id);
144 
145// New unique ID - store mapping externally
146const id = env.MY_DO.newUniqueId();
147const stub = env.MY_DO.get(id);
148```
149 
150## Storage Operations
151 
152```typescript
153// SQL (synchronous, recommended)
154this.ctx.storage.sql.exec("INSERT INTO t (c) VALUES (?)", value);
155const rows = this.ctx.storage.sql.exec<Row>("SELECT * FROM t").toArray();
156 
157// KV (async)
158await this.ctx.storage.put("key", value);
159const val = await this.ctx.storage.get<Type>("key");
160```
161 
162## Alarms
163 
164```typescript
165// Schedule (replaces existing)
166await this.ctx.storage.setAlarm(Date.now() + 60_000);
167 
168// Handler
169async alarm(): Promise<void> {
170 // Process scheduled work
171 // Optionally reschedule: await this.ctx.storage.setAlarm(...)
172}
173 
174// Cancel
175await this.ctx.storage.deleteAlarm();
176```
177 
178## Testing
179 
180Read the [testing reference](./references/testing.md) before configuring a suite or writing Durable Object tests. It routes to current setup, APIs, and examples and identifies the behavior to cover.
181 

Discussion