cloudflare-durable-objects
Build stateful Durable Objects for real-time apps, WebSocket servers, coordination, and persistent state. Use when: implementing chat rooms, multiplayer games, rate limiting, session management, WebSocket hibernation, or troubleshooting class export, migration, WebSocket state loss, or binding errors.
What this skill does
# Cloudflare Durable Objects **Status**: Production Ready ✅ **Last Updated**: 2025-11-23 **Dependencies**: cloudflare-worker-base (recommended) **Latest Versions**: [email protected], @cloudflare/[email protected] **Official Docs**: https://developers.cloudflare.com/durable-objects/ **Recent Updates (2025)**: - **Oct 2025**: WebSocket message size 1 MiB → 32 MiB, Data Studio UI for SQLite DOs (view/edit storage in dashboard) - **Aug 2025**: `getByName()` API shortcut for named DOs - **June 2025**: @cloudflare/actors library (beta) - recommended SDK with migrations, alarms, Actor class pattern - **May 2025**: Python Workers support for Durable Objects - **April 2025**: SQLite GA with 10GB storage (beta → GA, 1GB → 10GB), Free tier access - **Feb 2025**: PRAGMA optimize support, improved error diagnostics with reference IDs --- ## Quick Start **Scaffold new DO project:** ```bash npm create cloudflare@latest my-durable-app -- --template=cloudflare/durable-objects-template --ts ``` **Or add to existing Worker:** ```typescript // src/counter.ts - Durable Object class import { DurableObject } from 'cloudflare:workers'; export class Counter extends DurableObject { async increment(): Promise<number> { let value = (await this.ctx.storage.get<number>('value')) || 0; await this.ctx.storage.put('value', ++value); return value; } } export default Counter; // CRITICAL: Export required ``` ```jsonc // wrangler.jsonc - Configuration { "durable_objects": { "bindings": [{ "name": "COUNTER", "class_name": "Counter" }] }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["Counter"] } // SQLite backend (10GB limit) ] } ``` ```typescript // src/index.ts - Worker import { Counter } from './counter'; export { Counter }; export default { async fetch(request: Request, env: { COUNTER: DurableObjectNamespace<Counter> }) { const stub = env.COUNTER.getByName('global-counter'); // Aug 2025: getByName() shortcut return new Response(`Count: ${await stub.increment()}`); } }; ``` --- ## DO Class Essentials ```typescript import { DurableObject } from 'cloudflare:workers'; export class MyDO extends DurableObject { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); // REQUIRED first line // Load state before requests (optional) ctx.blockConcurrencyWhile(async () => { this.value = await ctx.storage.get('key') || defaultValue; }); } // RPC methods (recommended) async myMethod(): Promise<string> { return 'Hello'; } // HTTP fetch handler (optional) async fetch(request: Request): Promise<Response> { return new Response('OK'); } } export default MyDO; // CRITICAL: Export required // Worker must export DO class too import { MyDO } from './my-do'; export { MyDO }; ``` **Constructor Rules:** - ✅ Call `super(ctx, env)` first - ✅ Keep minimal - heavy work blocks hibernation wake - ✅ Use `ctx.blockConcurrencyWhile()` for storage initialization - ❌ Never `setTimeout`/`setInterval` (use alarms) - ❌ Don't rely on in-memory state with WebSockets (persist to storage) --- ## Storage API **Two backends available:** - **SQLite** (recommended): 10GB storage, SQL queries, atomic operations, PITR - **KV**: 128MB storage, key-value only **Enable SQLite in migrations:** ```jsonc { "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDO"] }] } ``` ### SQL API (SQLite backend) ```typescript export class MyDO extends DurableObject { sql: SqlStorage; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); this.sql = ctx.storage.sql; this.sql.exec(` CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY, text TEXT, created_at INTEGER); CREATE INDEX IF NOT EXISTS idx_created ON messages(created_at); PRAGMA optimize; // Feb 2025: Query performance optimization `); } async addMessage(text: string): Promise<number> { const cursor = this.sql.exec('INSERT INTO messages (text, created_at) VALUES (?, ?) RETURNING id', text, Date.now()); return cursor.one<{ id: number }>().id; } async getMessages(limit = 50): Promise<any[]> { return this.sql.exec('SELECT * FROM messages ORDER BY created_at DESC LIMIT ?', limit).toArray(); } } ``` **SQL Methods:** - `sql.exec(query, ...params)` → cursor - `cursor.one<T>()` → single row (throws if none) - `cursor.one<T>({ allowNone: true })` → row or null - `cursor.toArray<T>()` → all rows - `ctx.storage.transactionSync(() => { ... })` → atomic multi-statement **Rules:** Always use `?` placeholders, create indexes, use PRAGMA optimize after schema changes ### Key-Value API (both backends) ```typescript // Single operations await this.ctx.storage.put('key', value); const value = await this.ctx.storage.get<T>('key'); await this.ctx.storage.delete('key'); // Batch operations await this.ctx.storage.put({ key1: val1, key2: val2 }); const map = await this.ctx.storage.get(['key1', 'key2']); await this.ctx.storage.delete(['key1', 'key2']); // List and delete all const map = await this.ctx.storage.list({ prefix: 'user:', limit: 100 }); await this.ctx.storage.deleteAll(); // Atomic on SQLite only // Transactions await this.ctx.storage.transaction(async (txn) => { await txn.put('key1', val1); await txn.put('key2', val2); }); ``` **Storage Limits:** SQLite 10GB (April 2025 GA) | KV 128MB --- ## WebSocket Hibernation API **Capabilities:** - Thousands of WebSocket connections per instance - Hibernate when idle (~10s no activity) to save costs - Auto wake-up when messages arrive - **Message size limit**: 32 MiB (Oct 2025, up from 1 MiB) **How it works:** 1. Active → handles messages 2. Idle → ~10s no activity 3. Hibernation → in-memory state **cleared**, WebSockets stay connected 4. Wake → message arrives → constructor runs → handler called **CRITICAL:** In-memory state is **lost on hibernation**. Use `serializeAttachment()` to persist per-WebSocket metadata. ### Hibernation-Safe Pattern ```typescript export class ChatRoom extends DurableObject { sessions: Map<WebSocket, { userId: string; username: string }>; constructor(ctx: DurableObjectState, env: Env) { super(ctx, env); this.sessions = new Map(); // CRITICAL: Restore WebSocket metadata after hibernation ctx.getWebSockets().forEach((ws) => { this.sessions.set(ws, ws.deserializeAttachment()); }); } async fetch(request: Request): Promise<Response> { const pair = new WebSocketPair(); const [client, server] = Object.values(pair); const url = new URL(request.url); const metadata = { userId: url.searchParams.get('userId'), username: url.searchParams.get('username') }; // CRITICAL: Use ctx.acceptWebSocket(), NOT ws.accept() this.ctx.acceptWebSocket(server); server.serializeAttachment(metadata); // Persist across hibernation this.sessions.set(server, metadata); return new Response(null, { status: 101, webSocket: client }); } async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> { const session = this.sessions.get(ws); // Handle message (max 32 MiB since Oct 2025) } async webSocketClose(ws: WebSocket, code: number, reason: string, wasClean: boolean): Promise<void> { this.sessions.delete(ws); ws.close(code, 'Closing'); } async webSocketError(ws: WebSocket, error: any): Promise<void> { this.sessions.delete(ws); } } ``` **Hibernation Rules:** - ✅ `ctx.acceptWebSocket(ws)` - enables hibernation - ✅ `ws.serializeAttachment(data)` - persist metadata - ✅ `ctx.getWebSockets().forEach()` - restore in constructor - ✅ Use alarms instead of `setTimeout`/`setInterval` - ❌ `ws.accept()` - standard API, no hibernation - ❌ `setTimeout`/`setInterval` - prevents hibernation - ❌ In-progress `fetch()` - blocks hibernation --- ## Alarms API Schedule DO to wake at future time. **Use for:** batching, cleanup, reminders, periodic tasks. ```typescript export class Batcher extends DurableObject { async ad
Related in Cloud & DevOps
appbuilder-action-scaffolder
IncludedCreate, implement, deploy, and debug Adobe Runtime actions with consistent layout, validation, and error handling. Use this skill whenever the user needs to add actions to an App Builder project, understand action structure (params, response format, web/raw actions), configure actions in the manifest, use App Builder SDKs (State, Files, Events, database), deploy and invoke actions via CLI, debug action issues, or implement patterns such as webhook receivers, custom event providers, journaling consumers, large payload redirects, action sequence pipelines, and Asset Compute workers. Also trigger when users mention serverless functions in Adobe context, action logging, IMS authentication for actions, or cron-style scheduled actions.
orchestrating-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. Use this skill when the user needs a multi-step Data Cloud pipeline, cross-phase troubleshooting, or data space and data kit management. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase sf data360 workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching phase-specific skill), the task is STDM/session tracing/parquet telemetry (use observing-agentforce), standard CRM SOQL (use querying-soql), or Apex implementation (use generating-apex).
github-project-automation
IncludedAutomate GitHub repository setup with CI/CD workflows, issue templates, Dependabot, and CodeQL security scanning. Includes 12 production-tested workflows and prevents 18 errors: YAML syntax, action pinning, and configuration. Use when: setting up GitHub Actions CI/CD, creating issue/PR templates, enabling Dependabot or CodeQL scanning, deploying to Cloudflare Workers, implementing matrix testing, or troubleshooting YAML indentation, action version pinning, secrets syntax, runner versions, or CodeQL configuration. Keywords: github actions, github workflow, ci/cd, issue templates, pull request templates, dependabot, codeql, security scanning, yaml syntax, github automation, repository setup, workflow templates, github actions matrix, secrets management, branch protection, codeowners, github projects, continuous integration, continuous deployment, workflow syntax error, action version pinning, runner version, github context, yaml indentation error
sf-datacloud
IncludedSalesforce Data Cloud product orchestrator for connect→prepare→harmonize→segment→act workflows. TRIGGER when: user needs a multi-step Data Cloud pipeline, asks to set up or troubleshoot Data Cloud across phases, manages data spaces or data kits, or wants a cross-phase `sf data360` workflow. DO NOT TRIGGER when: work is isolated to a single phase (use the matching sf-datacloud-* skill), the task is STDM/session tracing/parquet telemetry (use sf-ai-agentforce-observability), standard CRM SOQL (use sf-soql), or Apex implementation (use sf-apex).
fabric-cli
IncludedUse this skill for Fabric.so CLI workflows with the `fabric` terminal command: diagnose/install/login, search or browse a Fabric library, save notes/links/files, create folders, ask the Fabric AI assistant, manage tasks/workspaces, generate shell completion, check subscription usage, produce JSON output, and use Fabric as persistent agent memory. Do not use for Microsoft Fabric/Azure/Power BI `fab`, Daniel Miessler's Fabric framework, Python Fabric SSH, Fabric.js, or textile/fashion fabric.
lark
IncludedLark/Feishu CLI skills: lark-cli operations for docs, markdown, sheets, base, calendar, im, mail, task, okr, drive, wiki, slides, whiteboard, apps, approval, attendance, contact, vc, minutes, event. Use when the user needs to operate Lark/Feishu resources via lark-cli, send messages, manage documents, spreadsheets, calendars, tasks, OKRs, deploy web pages, or any Feishu/Lark workspace operations.