cloudflare-durable-objects — quality + safety report
In the Skillier index (secondsky__cloudflare-durable-objects) · scanned 2026-06-03 · engine: builtin+triage
2 heuristic flags to review
Heuristic flags from the builtin scanner, which is known to over-flag (it trips on legitimate env-reading integrations, security skills, and library .eval calls). This is NOT an authoritative malicious verdict — re-scan with SkillSpector for the authoritative result. Run the authoritative scan →
📇 This skill is in the Skillier index (curated · deduped · quality-filtered). Install Skillier to route & load it into your AI client.
Quality notes
About this skill
Cloudflare Durable Objects for stateful coordination and real-time apps. Use for chat, multiplayer games, WebSocket hibernation, or encountering class export, migration, alarm errors.
📄 Read the SKILL.md
---
name: cloudflare-durable-objects
description: "Cloudflare Durable Objects for stateful coordination and real-time apps. Use for chat, multiplayer games, WebSocket hibernation, or encountering class export, migration, alarm errors."
metadata:
keywords:
- durable objects
- cloudflare do
- DurableObject class
- do bindings
- websocket hibernation
- do state api
- ctx.storage.sql
- ctx.acceptWebSocket
- webSocketMessage
- alarm() handler
- storage.setAlarm
- idFromName
- newUniqueId
- getByName
- DurableObjectStub
- serializeAttachment
- real-time cloudflare
- multiplayer cloudflare
- chat room workers
- coordination cloudflare
- stateful workers
- new_sqlite_classes
- do migrations
- location hints
- RPC methods
- blockConcurrencyWhile
- "\"do class export\""
- "\"new_sqlite_classes\""
- "\"migrations required\""
- "\"websocket hibernation\""
- "\"alarm api error\""
- "\"global uniqueness\""
- "\"binding not found\""
license: MIT
---
# Cloudflare Durable Objects
**Status**: Production Ready ✅
**Last Updated**: 2025-11-25
**Dependencies**: cloudflare-worker-base (recommended)
**Latest Versions**: wrangler@4.50.0+, @cloudflare/workers-types@4.20251125.0+
**Official Docs**: https://developers.cloudflare.com/durable-objects/
## Table of Contents
[What are Durable Objects?](#what-are-durable-objects) • [Quick Start](#quick-start-10-minutes) • [When to Load References](#when-to-load-references) • [Class Structure](#durable-object-class-structure) • [State API](#state-api---persistent-storage) • [WebSocket Hibernation](#websocket-hibernation-api) • [Alarms](#alarms-api---scheduled-tasks) • [RPC vs HTTP](#rpc-vs-http-fetch) • [Stubs & Routing](#creating-durable-object-stubs-and-routing) • [Migrations](#migrations---managing-do-classes) • [Common Patterns](#common-patterns) • [Critical Rules](#critical-rules) • [Known Issues](#known-issues-prevention)
## What are Durable Objects?
**Globally unique, stateful objects** with single-point coordination, strong consistency (ACID), WebSocket Hibernation (thousands of connections), SQLite storage (1GB), and alarms API.
**Use for:** Chat rooms, multiplayer games, rate limiting, session management, leader election, stateful workflows
---
## Quick Start (10 Minutes)
### Option 1: Scaffold New DO Project
```bash
npm create cloudflare@latest my-durable-app -- \
--template=cloudflare/durable-objects-template --ts --git --deploy false
cd my-durable-app && bun install && npm run dev
```
### Option 2: Add to Existing Worker
**1. Install types:**
```bash
bun add -d @cloudflare/workers-types
```
**2. Create DO class** (`src/counter.ts`):
```typescript
import { DurableObject } from 'cloudflare:workers';
export class Counter extends DurableObject {
async increment(): Promise<number> {
let value: number = (await this.ctx.storage.get('value')) || 0;
await this.ctx.storage.put('value', ++value);
return value;
}
}
export default Counter; // CRITICAL
```
**3. Configure** (`wrangler.jsonc`):
```jsonc
{
"durable_objects": {
"bindings": [{ "name": "COUNTER", "class_name": "Counter" }]
},
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["Counter"] }
]
}
```
**4. Call from Worker** (`src/index.ts`):
```typescript
import { Counter } from './counter';
interface Env {
COUNTER: DurableObjectNamespace<Counter>;
}
export { Counter };
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const stub = env.COUNTER.getByName('global-counter');
return new Response(`Count: ${await stub.increment()}`);
},
};
```
**Deploy:**
```bash
bunx wrangler deploy
```
---
## Available Commands
Use these interactive commands for guided workflows:
- **`/do-setup`** - Initialize new DO project with interactive setup wizard
- Choose storage backend (SQL, KV, both)
- Select use case pattern (WebSocket, Sessions, Rate Limiting, etc.)
- Optional Vitest testing setup
- Generates complete DO implementation
- **`/do-migrate`** - Interactive migration assistant
- New class creation (new_sqlite_classes, new_classes)
- Rename existing classes (renamed_classes)
- Delete classes with safety confirmations (deleted_classes)
- Transfer classes between scripts (transferred_classes)
- Auto-increments migration tags (v1, v2, v3...)
- **`/do-debug`** - Step-by-step debugging workflow
- Detects error categories (deployment, runtime, performance, etc.)
- Runs diagnostic checks on configuration and code
- Provides specific fixes with code examples
- Guides local testing and production verification
- **`/do-patterns`** - Pattern selection wizard
- Recommends DO pattern based on use case
- Supports WebSocket, Rate Limiting, Sessions, Analytics, Leader Election
- Generates complete pattern implementation
- Provides best practices and optimization tips
- **`/do-optimize`** - Performance optimization assistant
- Analyzes existing DO code for bottlenecks
- Provides targeted optimization recommendations
- Covers constructor, queries, WebSocket, memory, alarms
- Measures performance improvements
## Autonomous Agents
These agents work autonomously without user interaction:
- **`do-debugger`** - Automatic error detection and fixing
- Validates wrangler.jsonc configuration
- Detects 16+ common DO errors
- Applies fixes automatically with backups
- Tests fixes before reporting
- **`do-setup-assistant`** - Automatic project scaffolding
- Analyzes user requirements from natural language
- Generates complete DO implementation
- Creates tests, documentation, validation
- Supports all use case patterns
- **`do-pattern-implementer`** - Production pattern implementation
- Analyzes existing DO code
- Recommends patterns by priority
- Implements TTL cleanup, RPC metadata, SQL indexes, etc.
- Generates pattern-specific tests
---
## When to Load References
**Load immediately when user mentions:**
- **`state-api-reference.md`** → "storage", "sql", "database", "query", "get/put", "KV", "1GB limit"
- **`websocket-hibernation.md`** → "websocket", "real-time", "chat", "hibernation", "serializeAttachment"
- **`alarms-api.md`** → "alarms", "scheduled tasks", "cron", "periodic", "batch processing"
- **`rpc-patterns.md`** → "RPC", "fetch", "HTTP", "methods", "routing"
- **`rpc-metadata.md`** → "RpcTarget", "metadata", "DO name", "idFromName access"
- **`stubs-routing.md`** → "stubs", "idFromName", "newUniqueId", "location hints", "jurisdiction"
- **`migrations-guide.md`** → "migrations", "rename", "delete", "transfer", "schema changes"
- **`migration-cheatsheet.md`** → "migration quick reference", "migration types", "common migrations"
- **`common-patterns.md`** → "patterns", "examples", "rate limiting", "sessions", "leader election"
- **`vitest-testing.md`** → "test", "testing", "vitest", "unit test", "@cloudflare/vitest-pool-workers"
- **`gradual-deployments.md`** → "gradual", "deployment", "traffic split", "rollout", "canary"
- **`typescript-config.md`** → "TypeScript", "types", "tsconfig", "wrangler.jsonc", "bindings"
- **`advanced-sql-patterns.md`** → "CTE", "window functions", "FTS5", "full-text search", "JSON functions", "complex SQL"
- **`security-best-practices.md`** → "security", "authentication", "authorization", "SQL injection", "CORS", "encryption", "rate limiting"
- **`error-codes.md`** → "error codes", "error catalog", "specific error", "E001", "troubleshooting"
- **`top-errors.md`** → errors, "not working", debugging, "binding not found"
**Load proactively when:**
- Building new feature → Load relevant pattern from `common-patterns.md`
- Debugging issue → Load `error-codes.md` for specific errors, then `top-errors.md`
- Implementing WebSocket → Load `websocket-hibernation.md` before coding
- Setting up storage → Load `state-api-reference.md` for SQL/KV APIs
- Complex SQL queries → Load `advanced-sql-patterns.md` for CTEs, window functions, FTS5
- Security review → Load `security-best-practices.md` for authentication, authorization, SQL injection prevention
- Creating first DO → Load `stubs-routing.md` for ID methods
- Writing tests → Load `vitest-testing.md` for testing patterns
- Planning deployment → Load `gradual-deployments.md` for rollout strategy
- Migration needed → Load `migration-cheatsheet.md` for quick reference
- Using DO name inside DO → Load `rpc-metadata.md` for RpcTarget pattern
- TypeScript configuration → Load `typescript-config.md` for setup
---
## Durable Object Class Structure
All DOs extend `DurableObject` and **MUST be exported**:
```typescript
import { DurableObject } from 'cloudflare:workers';
export class MyDO extends DurableObject {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env); // Required first line
// Keep minimal - heavy work blocks hibernation
ctx.blockConcurrencyWhile(async () => {
// Load from storage before handling requests
});
}
async myMethod(): Promise<string> { // RPC method (recommended)
return 'Hello!';
}
}
export default MyDO; // CRITICAL: Must export
```
**`this.ctx` provides:** `storage` (SQL/KV), `id` (unique ID), `waitUntil()`, `acceptWebSocket()`
---
## State API - Persistent Storage
Durable Objects provide two storage options:
**SQL API** (SQLite backend, **recommended**):
- Access via `ctx.storage.sql`
- Up to 1GB storage per instance
- SQL queries with transactions, indexes, cursors
- Atomic operations (deleteAll is all-or-nothing)
- Use `new_sqlite_classes` in migrations
**Key-Value API** (available on both backends):
- Access via `ctx.storage` (get/put/delete/list)
- Simple key-value operations
- Async transactions supported
- 128MB limit on KV backend, 1GB on SQLite
**Quick example**:
```typescript
export class Counter 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 counts (key TEXT PRIMARY KEY, value INTEGER)');
}
async increment(): Promise<number> {
this.sql.exec('INSERT OR REPLACE INTO counts (key, value) VALUES (?, ?)', 'count', 1);
return this.sql.exec('SELECT value FROM counts WHERE key = ?', 'count').one<{value: number}>().value;
}
}
```
**Load `references/state-api-reference.md` for complete SQL and KV API documentation, cursor operations, transactions, parameterized queries, storage limits, and migration patterns.**
---
## WebSocket Hibernation API
Handle **thousands of WebSocket connections** per DO instance with automatic hibernation when idle (~10s no activity), saving duration costs. Connections stay open at the edge while DO sleeps.
**CRITICAL Rules**:
- ✅ Use `ctx.acceptWebSocket(server)` (enables hibernation)
- ✅ Use `ws.serializeAttachment(data)` to persist metadata across hibernation
- ✅ Restore connections in constructor with `ctx.getWebSockets()`
- ❌ Don't use `ws.accept()` (standard API, no hibernation)
- ❌ Don't use `setTimeout`/`setInterval` (prevents hibernation)
**Handler methods**: `webSocketMessage()`, `webSocketClose()`, `webSocketError()`
**Quick pattern**:
```typescript
export class ChatRoom extends DurableObject {
sessions: Map<WebSocket, any>;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.sessions = new Map();
// Restore connections 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);
this.ctx.acceptWebSocket(server); // ← Enables hibernation
server.serializeAttachment({ userId: 'alice' }); // ← Persists across hibernation
this.sessions.set(server, { userId: 'alice' });
return new Response(null, { status: 101, webSocket: cl
… (truncated)Want a live grade + an embeddable README badge? Run your skill through the free scanner.
Graded independently by Skillproof — nothing to sell the author. Quality is mechanical + corpus-grounded; safety flags are heuristic (builtin+triage), not a malicious verdict.