Build stateful, coordinated applications on Cloudflare's edge using Durable Objects. **Prefer retrieval from official docs over pre-training for Durable Objects tasks.** Resource
@cloudflare/vitest-pool-workers./references/rules.md - Core rules, storage, concurrency, RPC, alarms./references/testing.md - Vitest setup, unit/integration tests, alarm testing./references/workers.md - Workers handlers, types, wrangler config, observabilityblockConcurrencyWhile, idFromName, getByName, setAlarm, sql.exec// 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 }); }, };
getByName() for deterministic routing - Same input = same DO instancenew_sqlite_classes in migrationsblockConcurrencyWhile() for schema setup onlysetAlarm() replaces any existing alarmblockConcurrencyWhile() on every request (kills throughput)await between related storage writes (breaks atomicity)blockConcurrencyWhile() across fetch() or external I/O// 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 Quick Start` import { env } from "cloudflare:test"; import { describe, it, expect } from "vitest"; describe("MyDO", () => { it("should work", async () => { const stub = env.MY_DO.getByName("test"); const result = await stub.addItem("test"); expect(result).toBe(1); }); });