cloudflare-durable-objects

Cloudflare Durable Objects

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cloudflare-durable-objects" with this command: npx skills add itechmeat/llm-code/itechmeat-llm-code-cloudflare-durable-objects

Cloudflare Durable Objects

Durable Objects combine compute with strongly consistent, transactional storage. Each object has a globally-unique name, enabling coordination across clients worldwide.

Quick Start

Durable Object Class

// src/counter.ts import { DurableObject } from "cloudflare:workers";

export class Counter extends DurableObject<Env> { async increment(): Promise<number> { let count = (await this.ctx.storage.get<number>("count")) ?? 0; count++; await this.ctx.storage.put("count", count); return count; }

async getCount(): Promise<number> { return (await this.ctx.storage.get<number>("count")) ?? 0; } }

Worker Entry Point

// src/index.ts export { Counter } from "./counter";

export default { async fetch(request: Request, env: Env): Promise<Response> { const id = env.COUNTER.idFromName("global"); const stub = env.COUNTER.get(id); const count = await stub.increment(); return new Response(Count: ${count}); }, };

wrangler.jsonc

{ "name": "counter-worker", "main": "src/index.ts", "durable_objects": { "bindings": [ { "name": "COUNTER", "class_name": "Counter" } ] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["Counter"] } ] }

Deploy

npx wrangler deploy

Core Concepts

DurableObjectState

Available as this.ctx in Durable Object class:

interface DurableObjectState { readonly id: DurableObjectId; readonly storage: DurableObjectStorage;

blockConcurrencyWhile<T>(callback: () => Promise<T>): Promise<T>; waitUntil(promise: Promise<any>): void; // No effect in DO

// WebSocket Hibernation acceptWebSocket(ws: WebSocket, tags?: string[]): void; getWebSockets(tag?: string): WebSocket[]; getTags(ws: WebSocket): string[]; setWebSocketAutoResponse(pair?: WebSocketRequestResponsePair): void; getWebSocketAutoResponse(): WebSocketRequestResponsePair | null;

abort(message?: string): void; // Force reset DO }

DurableObjectId

const id = env.MY_DO.idFromName("user-123"); // Deterministic ID const id = env.MY_DO.newUniqueId(); // Random unique ID const stub = env.MY_DO.get(id); // Get stub for DO instance

See api.md for full type definitions.

Storage API (SQLite-backed)

SQLite is the recommended storage backend for new Durable Objects.

SQL API

const cursor = this.ctx.storage.sql.exec("SELECT * FROM users WHERE id = ?", userId);

// Get single row (throws if not exactly one) const user = cursor.one();

// Get all rows const users = cursor.toArray();

// Iterate for (const row of cursor) { console.log(row); }

SQL Cursor Properties

cursor.columnNames; // string[] cursor.rowsRead; // number cursor.rowsWritten; // number

Create Tables

this.ctx.storage.sql.exec( CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, name TEXT NOT NULL, created_at INTEGER DEFAULT (unixepoch()) ));

Insert/Update

this.ctx.storage.sql.exec("INSERT INTO users (id, name) VALUES (?, ?)", id, name);

this.ctx.storage.sql.exec("UPDATE users SET name = ? WHERE id = ?", newName, id);

Transactions

// Synchronous transaction (SQLite only) this.ctx.storage.transactionSync(() => { this.ctx.storage.sql.exec("INSERT INTO logs (msg) VALUES (?)", "start"); this.ctx.storage.sql.exec("UPDATE counters SET value = value + 1"); });

Database Size

const sizeBytes = this.ctx.storage.sql.databaseSize;

See storage.md for KV API and advanced usage.

Storage API (KV)

Synchronous KV (SQLite-backed)

this.ctx.storage.kv.put("key", value); const val = this.ctx.storage.kv.get("key"); const deleted = this.ctx.storage.kv.delete("key");

for (const [key, value] of this.ctx.storage.kv.list()) { console.log(key, value); }

Async KV (Both backends)

await this.ctx.storage.put("key", value); const val = await this.ctx.storage.get<MyType>("key");

// Batch operations (up to 128 keys) const values = await this.ctx.storage.get(["key1", "key2", "key3"]); await this.ctx.storage.put({ key1: val1, key2: val2 }); await this.ctx.storage.delete(["key1", "key2"]);

// List with options const map = await this.ctx.storage.list({ prefix: "user:" });

// Delete all await this.ctx.storage.deleteAll();

Write Coalescing

Multiple writes without await are coalesced atomically:

// These are batched into single transaction this.ctx.storage.put("a", 1); this.ctx.storage.put("b", 2); this.ctx.storage.put("c", 3); // All committed together

Alarms

Schedule single alarm per Durable Object for background processing.

Set Alarm

// Schedule 1 hour from now await this.ctx.storage.setAlarm(Date.now() + 60 * 60 * 1000);

// Schedule at specific time await this.ctx.storage.setAlarm(new Date("2024-12-31T00:00:00Z"));

Handle Alarm

export class MyDO extends DurableObject<Env> { async alarm(info?: AlarmInfo): Promise<void> { console.log(Alarm fired! Retry: ${info?.isRetry}, count: ${info?.retryCount});

// Process scheduled work
await this.processScheduledTasks();

// Schedule next alarm if needed
const nextRun = await this.getNextScheduledTime();
if (nextRun) {
  await this.ctx.storage.setAlarm(nextRun);
}

} }

Alarm Methods

await this.ctx.storage.setAlarm(timestamp); // Set/overwrite alarm const time = await this.ctx.storage.getAlarm(); // Get scheduled time (ms) or null await this.ctx.storage.deleteAlarm(); // Cancel alarm

Retry behavior: Alarms retry with exponential backoff (2s initial, up to 6 retries) on exceptions.

See alarms.md for patterns.

WebSocket Hibernation

Keep WebSocket connections alive while Durable Object hibernates.

Accept WebSocket

export class ChatRoom extends DurableObject<Env> { async fetch(request: Request): Promise<Response> { const upgradeHeader = request.headers.get("Upgrade"); if (upgradeHeader === "websocket") { const [client, server] = Object.values(new WebSocketPair());

  // Accept with hibernation support
  this.ctx.acceptWebSocket(server, ["user:123"]); // Optional tags

  return new Response(null, { status: 101, webSocket: client });
}
return new Response("Expected WebSocket", { status: 400 });

}

async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> { // Handle incoming message (DO wakes from hibernation) this.broadcast(message); }

async webSocketClose(ws: WebSocket, code: number, reason: string): Promise<void> { // Handle disconnect } }

Broadcast to All

broadcast(message: string) { for (const ws of this.ctx.getWebSockets()) { ws.send(message); } }

Per-Connection State

// Save state that survives hibernation (max 2048 bytes) ws.serializeAttachment({ userId: "123", role: "admin" });

// Restore in message handler const state = ws.deserializeAttachment();

Auto-Response (No Wake)

// Respond to pings without waking DO this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair("ping", "pong"));

See websockets.md for details.

RPC Methods

Call Durable Object methods directly (compatibility date >= 2024-04-03):

const stub = env.USER_SERVICE.get(id); const user = await stub.getUser("123"); // Direct RPC call await stub.updateUser("123", { name: "New Name" });

Note: Each RPC method call = one RPC session for billing.

Initialization

Use blockConcurrencyWhile in constructor for migrations:

constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); ctx.blockConcurrencyWhile(async () => { this.ctx.storage.sql.exec(CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT)); }); }

Timeout: 30 seconds.

Hibernation

Conditions for Hibernation

All must be true:

  • No pending setTimeout /setInterval

  • No in-progress await fetch()

  • Using Hibernation WebSocket API (not standard WebSocket)

  • No active request processing

Lifecycle

  • Active: Processing requests

  • Idle hibernateable: ~10 seconds → may hibernate

  • Hibernated: Removed from memory, WebSockets stay connected

  • Wake: On message/event, constructor runs, handler invoked

Important: In-memory state is lost on hibernation. Restore from storage or attachments.

Bindings & Migrations

{ "durable_objects": { "bindings": [{ "name": "MY_DO", "class_name": "MyDO" }] }, "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDO"] }] }

Note: Cannot enable SQLite on existing deployed classes.

Wrangler Commands

npx wrangler deploy # Deploy with migrations wrangler tail # Tail logs

Limits

Feature Free Paid

DO classes 100 500

Storage per DO 10 GB 10 GB

Storage per account 5 GB Unlimited

CPU per request 30 sec 30 sec (max 5 min)

WebSocket connections 32,768 32,768

SQL row/value size 2 MB 2 MB

KV value size 128 KiB 128 KiB

Batch size 128 keys 128 keys

Pricing

Metric Free Paid

Requests 100K/day 1M/mo included, +$0.15/M

Duration 13K GB-s/day 400K GB-s/mo, +$12.50/M GB-s

SQLite rows read 5M/day 25B/mo included, +$0.001/M

SQLite rows written 100K/day 50M/mo included, +$1.00/M

Storage 5 GB 5 GB/mo included, +$0.20/GB-mo

WebSocket: 20:1 billing ratio (1M messages = 50K requests).

See pricing.md for details.

Prohibitions

  • ❌ Do not store state outside storage (lost on hibernation)

  • ❌ Do not use standard WebSocket API for hibernation

  • ❌ Do not exceed 2 MB per row/value in SQLite

  • ❌ Do not call sql.exec() with transaction control statements

  • ❌ Do not expect waitUntil to work (no effect in DO)

References

  • api.md — Full API reference

  • storage.md — Storage API details

  • websockets.md — WebSocket hibernation

  • alarms.md — Alarm patterns

  • pricing.md — Billing details

Links

  • Documentation

  • Changelog

Related Skills

  • cloudflare-workers — Worker development

  • cloudflare-d1 — D1 database

  • cloudflare-kv — Global KV

  • cloudflare-workflows — Durable execution

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

react-testing-library

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

social-writer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

commits

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

mantine-dev

No summary provided by upstream source.

Repository SourceNeeds Review