cloudflare-durable-objects について
Durable Objects を使う Cloudflare Workers 開発者向けのスキルです。永続状態や協調処理、WebSocket のハイバネーションを扱い、セットアップ、移行、デバッグ、最適化のガイドと自律型エージェントを備えています。
主な機能
- 01プロジェクトのひな型と設定ガイド
- 02SQL とキー・バリュー保存のリファレンス
- 03WebSocket ハイバネーションのパターン
- 04アラームと RPC のパターン
- 05移行、デバッグ、最適化のワークフロー
- 06自動修正とパターン実装
活用シーン
- 01チャットルームやセッション管理の作成
- 02レート制限やリーダー選出の実装
- 03Durable Objects の移行設定
- 04既存実装のデバッグや最適化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill cloudflare-durable-objectsインストール後、Claude Code で/cloudflare-durable-objectsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/advanced-sql-patterns.mdalarms-api.mdbest-practices.mdcommon-patterns.mddata-modeling.mderror-codes.mdgradual-deployments.mdmigration-cheatsheet.mdmigrations-guide.mdmonitoring-debugging.mdperformance-optimization.mdrpc-metadata.mdrpc-patterns.mdsecurity-best-practices.mdstate-api-reference.mdstubs-routing.mdtop-errors.mdtypescript-config.mdvitest-testing.mdwebsocket-hibernation.mdwrangler-commands.mdscripts/check-versions.shmigration-generator.shsetup-vitest-do.shvalidate-do-config.shtemplates/alarms-api-do.tsbasic-do.tsgradual-deployment-config.jsonclocation-hints.tsmulti-do-coordination.ts詳細
Cloudflare Durable Objects
ステータス:本番環境対応 ✅ 最終更新日:2025-11-25 依存関係:cloudflare-worker-base(推奨) 最新バージョン:[email protected]+、@cloudflare/[email protected]+ 公式ドキュメント:https://developers.cloudflare.com/durable-objects/
目次
Durable Objects とは? • クイックスタート • 参照を読み込むタイミング • クラス構造 • 状態 API • WebSocket ハイバネーション • アラーム • RPC と HTTP の比較 • スタブとルーティング • マイグレーション • 一般的なパターン • 重要なルール • 既知の問題
Durable Objects とは?
グローバルに一意で、状態を持つオブジェクトです。単一の調整ポイント、強整合性(ACID)、WebSocket ハイバネーション(数千の接続)、SQLite ストレージ(1GB)、アラーム API を備えています。
**用途:**チャットルーム、マルチプレイヤーゲーム、レート制限、セッション管理、リーダー選出、状態を持つワークフロー
クイックスタート(10 分)
オプション 1:新しい DO プロジェクトをスキャフォールドする
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
オプション 2:既存の Worker に追加する
1. 型をインストールする:
bun add -d @cloudflare/workers-types
2. DO クラスを作成する(src/counter.ts):
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. 設定する(wrangler.jsonc):
{
"durable_objects": {
"bindings": [{ "name": "COUNTER", "class_name": "Counter" }]
},
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["Counter"] }
]
}
4. Worker から呼び出す(src/index.ts):
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()}`);
},
};
デプロイ:
bunx wrangler deploy
利用可能なコマンド
ガイド付きワークフローには、次の対話型コマンドを使用します。
-
/do-setup- 対話型セットアップウィザードで新しい DO プロジェクトを初期化します- ストレージバックエンドを選択します(SQL、KV、両方)
- ユースケースのパターンを選択します(WebSocket、セッション、レート制限など)
- Vitest のテスト設定は任意です
- 完全な DO の実装を生成します
-
/do-migrate- 対話型マイグレーションアシスタント- クラスを新規作成します(new_sqlite_classes、new_classes)
- 既存のクラス名を変更します(renamed_classes)
- 安全確認のうえクラスを削除します(deleted_classes)
- スクリプト間でクラスを移行します(transferred_classes)
- マイグレーションタグを自動で連番にします(v1、v2、v3...)
-
/do-debug- ステップごとのデバッグワークフロー- エラーのカテゴリを検出します(デプロイ、実行時、パフォーマンスなど)
- 設定とコードの診断チェックを実行します
- コード例を含む具体的な修正方法を提示します
- ローカルテストと本番環境での検証をガイドします
-
/do-patterns- パターン選択ウィザード- ユースケースに基づいて DO パターンを推奨します
- WebSocket、レート制限、セッション、分析、リーダー選出に対応します
- 完全なパターン実装を生成します
- ベストプラクティスと最適化のヒントを提示します
-
/do-optimize- パフォーマンス最適化アシスタント- 既存の DO コードを分析し、ボトルネックを特定します
- 対象を絞った最適化の推奨事項を提示します
- コンストラクター、クエリ、WebSocket、メモリ、アラームを対象とします
- パフォーマンスの改善を測定します
自律型エージェント
これらのエージェントは、ユーザーとのやり取りなしに自律的に動作します。
-
do-debugger- エラーを自動で検出して修正します- wrangler.jsonc の設定を検証します
- DO でよくあるエラーを 16 種類以上検出します
- バックアップを作成したうえで修正を自動適用します
- 報告前に修正をテストします
-
do-setup-assistant- プロジェクトのスキャフォールディングを自動化します- 自然言語からユーザーの要件を分析します
- 完全な DO の実装を生成します
- テスト、ドキュメント、検証を作成します
- すべてのユースケースパターンに対応します
-
do-pattern-implementer- 本番環境向けのパターンを実装します- 既存の DO コードを分析します
- 優先度に基づいてパターンを推奨します
- TTL クリーンアップ、RPC メタデータ、SQL インデックスなどを実装します
- パターンに応じたテストを生成します
リファレンスを読み込むタイミング
ユーザーが次の内容に言及したら、すぐに読み込みます:
state-api-reference.md→ 「ストレージ」、「sql」、「データベース」、「クエリ」、「get/put」、「KV」、「1GB の制限」websocket-hibernation.md→ 「websocket」、「リアルタイム」、「チャット」、「休止」、「serializeAttachment」alarms-api.md→ 「アラーム」、「スケジュールされたタスク」、「cron」、「定期的」、「バッチ処理」rpc-patterns.md→ 「RPC」、「fetch」、「HTTP」、「メソッド」、「ルーティング」rpc-metadata.md→ 「RpcTarget」、「メタデータ」、「DO 名」、「idFromName アクセス」stubs-routing.md→ 「スタブ」、「idFromName」、「newUniqueId」、「ロケーションヒント」、「管轄区域」migrations-guide.md→ 「マイグレーション」、「名前の変更」、「削除」、「移行」、「スキーマの変更」migration-cheatsheet.md→ 「マイグレーションのクイックリファレンス」、「マイグレーションの種類」、「一般的なマイグレーション」common-patterns.md→ 「パターン」、「例」、「レート制限」、「セッション」、「リーダー選出」vitest-testing.md→ 「テスト」、「テスト」、「vitest」、「単体テスト」、「@cloudflare/vitest-pool-workers」gradual-deployments.md→ 「段階的」、「デプロイ」、「トラフィック分割」、「ロールアウト」、「カナリア」typescript-config.md→ 「TypeScript」、「型」、「tsconfig」、「wrangler.jsonc」、「バインディング」advanced-sql-patterns.md→ 「CTE」、「ウィンドウ関数」、「FTS5」、「全文検索」、「JSON 関数」、「複雑な SQL」security-best-practices.md→ 「セキュリティ」、「認証」、「認可」、「SQL インジェクション」、「CORS」、「暗号化」、「レート制限」error-codes.md→ 「エラーコード」、「エラーカタログ」、「特定のエラー」、「E001」、「トラブルシューティング」top-errors.md→ エラー、「動作しない」、デバッグ、「バインディングが見つからない」
次の場合は、先回りして読み込みます:
- 新機能の構築 →
common-patterns.mdから関連するパターンを読み込みます - 問題のデバッグ → 特定のエラーについて
error-codes.mdを読み込み、その後top-errors.mdを読み込みます - WebSocket の実装 → コーディング前に
websocket-hibernation.mdを読み込みます - ストレージの設定 → SQL/KV API について
state-api-reference.mdを読み込みます - 複雑な SQL クエリ → CTE、ウィンドウ関数、FTS5 について
advanced-sql-patterns.mdを読み込みます - セキュリティレビュー → 認証、認可、SQL インジェクション対策について
security-best-practices.mdを読み込みます - 初めての DO の作成 → ID メソッドについて
stubs-routing.mdを読み込みます - テストの作成 → テストパターンについて
vitest-testing.mdを読み込みます - デプロイの計画 → ロールアウト戦略について
gradual-deployments.mdを読み込みます - マイグレーションが必要 → クイックリファレンスとして
migration-cheatsheet.mdを読み込みます - DO 内で DO 名を使用 → RpcTarget パターンについて
rpc-metadata.mdを読み込みます - TypeScript の設定 → セットアップについて
typescript-config.mdを読み込みます
Durable Object クラスの構造
すべての DO は DurableObject を継承し、必ずエクスポートする必要があります:
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 が提供するもの: storage(SQL/KV)、id(一意の ID)、waitUntil()、acceptWebSocket()
State API - 永続ストレージ
Durable Objects には、2 つのストレージオプションがあります:
SQL API(SQLite バックエンド、推奨):
ctx.storage.sqlからアクセス- インスタンスごとに最大 1GB のストレージ
- トランザクション、インデックス、カーソルを使用した SQL クエリ
- アトミック操作(deleteAll はすべて成功するか、すべて失敗します)
- マイグレーションでは
new_sqlite_classesを使用します
Key-Value API(両方のバックエンドで利用可能):
ctx.storageからアクセス(get/put/delete/list)- シンプルなキーと値の操作
- 非同期トランザクションに対応
- KV バックエンドでは 128MB、SQLite では 1GB の制限
簡単な例:
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;
}
}
SQL と KV の API ドキュメント全体、カーソル操作、トランザクション、パラメーター化クエリ、ストレージ制限、マイグレーションパターンについては、references/state-api-reference.md を読み込んでください。
WebSocket ハイバネーション API
DO インスタンスごとに数千の WebSocket 接続を処理し、アイドル状態(約 10 秒間アクティビティがない状態)になると自動的にハイバネーションして、継続時間に応じたコストを削減します。DO がスリープ中も、接続はエッジで開いたままです。
重要なルール:
- ✅
ctx.acceptWebSocket(server)を使用します(ハイバネーションを有効にします) - ✅
ws.serializeAttachment(data)を使用して、ハイバネーション中もメタデータを保持します - ✅ コンストラクターで
ctx.getWebSockets()を使用して接続を復元します - ❌
ws.accept()は使用しないでください(標準 API のため、ハイバネーションに対応していません) - ❌
setTimeout/setIntervalは使用しないでください(ハイバネーションを妨げます)
ハンドラーメソッド:webSocketMessage()、webSocketClose()、webSocketError()
クイックパターン:
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: client });
}
async webSocketMessage(ws: WebSocket, message: string): Promise<void> {
const session = this.sessions.get(ws);
// Broadcast to all
this.sessions.forEach((_, w) => w.send(message));
}
}
ハンドラーパターン、ハイバネーションのライフサイクル、serializeAttachment API、接続管理、ブロードキャストパターン、ハイバネーションのトラブルシューティングについて詳しくは、references/websocket-hibernation.md を読み込んでください。
Alarms API - スケジュールタスク
バッチ処理、クリーンアップ、リマインダー、定期タスクのために、将来の時刻に DO を起動するようスケジュールします。
コア API:
await ctx.storage.setAlarm(timestamp)- アラームをスケジュールしますawait ctx.storage.getAlarm()- 現在のアラーム時刻を取得します(設定されていない場合は null)await ctx.storage.deleteAlarm()- アラームをキャンセルしますasync alarm(info)- アラーム発火時に呼び出されるハンドラー
主な機能:
- ✅ 自動再試行(最大 6 回)により、少なくとも 1 回の実行を保証します
- ✅ ハイバネーションやエビクション後も維持されます
- ✅ 実行が成功すると自動的に削除されます
- ⚠️ DO ごとに設定できるアラームは 1 つのみです(新しいアラームを設定すると、以前のものが上書きされます)
クイックパターン:
export class Batcher extends DurableObject {
async addItem(item: string): Promise<void> {
await this.ctx.storage.put('items', [...existingItems, item]);
// Schedule batch processing if not already scheduled
if (await this.ctx.storage.getAlarm() === null) {
await this.ctx.storage.setAlarm(Date.now() + 10000); // 10 seconds
}
}
async alarm(info: { retryCount: number; isRetry: boolean }): Promise<void> {
const items = await this.ctx.storage.get('items');
await this.processBatch(items); // Send to API, write to DB, etc.
await this.ctx.storage.put('items', []); // Clear buffer
}
}
定期アラームのパターン、再試行の処理、エラーの発生ケース、クリーンアップジョブ、バッチ処理戦略について詳しくは、references/alarms-api.md を読み込んでください。
RPC と HTTP Fetch の比較
RPC(推奨):await stub.increment() のように DO メソッドを直接呼び出します。型安全でシンプルであり、自動シリアライズに対応しています。compatibility_date >= 2024-04-03 が必要です。
HTTP Fetch:async fetch(request) ハンドラーを使用する、従来の HTTP リクエスト/レスポンス方式です。WebSocket のアップグレードに必要です。
簡単な比較:
// RPC Pattern (simpler)
export class Counter extends DurableObject {
async increment(): Promise<number> { // ← Direct method
let value = await this.ctx.storage.get<number>('count') || 0;
return ++value;
}
}
const count = await stub.increment(); // ← Direct call
// HTTP Fetch Pattern
export class Counter extends DurableObject {
async fetch(request: Request): Promise<Response> { // ← HTTP handler
const url = new URL(request.url);
if (url.pathname === '/increment') { /* ... */ }
}
}
const response = await stub.fetch('/increment', { method: 'POST' });
RPC を使用する場合:新規プロジェクト、型安全性、シンプルなメソッド呼び出し
HTTP Fetch を使用する場合:WebSocket のアップグレード、複雑なルーティング、レガシーコード
RPC と Fetch の詳しい比較、移行ガイド、エラー処理パターン、メソッドの公開範囲の制御については、references/rpc-patterns.md を読み込んでください。
Durable Object のスタブとルーティングの作成
Worker から Durable Object を操作するには、ID を取得 → スタブを作成 → メソッドを呼び出す、の順に行います。
ID の作成方法は 3 つあります:
idFromName(name)- 名前付き DO(最も一般的):グローバルに同じインスタンスへ決定的にルーティングしますnewUniqueId()- ランダム ID:新しい一意のインスタンスを作成します。後でアクセスできるように ID を保存する必要がありますidFromString(idString)- 保存した ID 文字列から再作成します
スタブの取得:
// Method 1: From ID
const id = env.CHAT_ROOM.idFromName('room-123');
const stub = env.CHAT_ROOM.get(id);
// Method 2: Shortcut for named DOs (recommended)
const stub = env.CHAT_ROOM.getByName('room-123');
await stub.myMethod();
ロケーションヒントを使用した地理的ルーティング:
- スタブの作成時に
locationHintオプションを設定します:{ locationHint: 'enam' } - 9 つのリージョン:wnam、enam、sam、weur、eeur、apac、oc、afr、me
- ベストエフォートであり、保証はありません。初回作成時のみ影響します
管轄区域の制限によるデータレジデンシー:
newUniqueId({ jurisdiction: 'eu' })または{ jurisdiction: 'fedramp' }を使用します- 厳格に適用されます(DO は管轄区域の外に出ません)
- ロケーションヒントとは併用できません
- GDPR/FedRAMP への準拠に必要です
ID の各種メソッド、スタブ管理、ロケーションヒント、管轄区域の制限、ユースケース、ベストプラクティス、エラー処理パターンについて詳しくは、references/stubs-routing.md を読み込んでください。
マイグレーション - DO クラスの管理
DO クラスを作成、名前変更、削除、または Worker 間で移行する場合は、マイグレーションが必須です。
4 種類のマイグレーション:
- 新しい DO の作成:
new_sqlite_classes(推奨、1GB)またはnew_classes(従来の KV、128MB)を使用します - DO の名前変更:
from/toマッピングを指定してrenamed_classesを使用します(データは保持され、バインディングは引き継がれます) - DO の削除:
deleted_classesを使用します(⚠️ 即時削除され、取り消せません。すべてのストレージが失われます) - DO の移行:
from_scriptを指定してtransferred_classesを使用します(インスタンスを新しい Worker に移動します)
簡単な例 - SQLite を使用した新しい DO の作成:
{
"durable_objects": {
"bindings": [{ "name": "COUNTER", "class_name": "Counter" }]
},
"migrations": [
{
"tag": "v1", // Unique identifier (append-only)
"new_sqlite_classes": ["Counter"]
}
]
}
重要なルール:
- ❌ マイグレーションはアトミックです(すべてのインスタンスが一度に移行され、段階的なロールアウトはできません)
- ❌ 既存の KV ベースの DO で SQLite を有効にすることはできません(新しいクラスを作成する必要があります)
- ❌ マイグレーションタグは一意である必要があります(再利用できず、追加のみ可能です)
- ✅ コードの変更にマイグレーションは不要です(必要なのはスキーマの変更のみです)
- ✅ DO クラス名はアカウント内で一意です(すべての Worker を通じて)
完全なマイグレーションパターン、名前変更/削除/移行の手順、ロールバック戦略、マイグレーション時の注意点については、references/migrations-guide.md を読み込んでください。
一般的なパターン
Cloudflare Durable Objects 向けの、本番環境対応の4つのパターン:
- レート制限 - スライディングウィンドウによるユーザーごとのレート制限と、リクエスト追跡のための KV ストレージ
- セッション管理 - TTL、SQL ストレージ、アラームによる自動クリーンアップを備えたユーザーセッション
- リーダー選出 - SQL 制約とハートビート機構による単一リーダーの保証
- 複数 DO の協調 - ゲームコーディネーターとゲームルームのパターン、親子 DO 関係
簡単な例 - レートリミッター:
export class RateLimiter extends DurableObject {
async checkLimit(userId: string, limit: number, window: number): Promise<boolean> {
const requests = await this.ctx.storage.get<number[]>(`rate:${userId}`) || [];
const validRequests = requests.filter(t => Date.now() - t < window);
if (validRequests.length >= limit) return false;
validRequests.push(Date.now());
await this.ctx.storage.put(`rate:${userId}`, validRequests);
return true;
}
}
すべての4つのパターンについて、完全なコード例、SQL スキーマ、アラームの使用方法、エラー処理、ベストプラクティスを含む完全な実装は、references/common-patterns.md を読み込んでください。
重要なルール
✅ 必ず行うこと:
- DO クラスをエクスポートします:
export default MyDO - コンストラクターでは最初に
super(ctx, env)を呼び出します - マイグレーションでは
new_sqlite_classesを使用します(KV の128MBに対して1GB) - ハイバネーションには
ctx.acceptWebSocket()を使用します(ws.accept()ではありません) - 状態をストレージに永続化します(メモリのみに保存しません)
- setTimeout/setInterval の代わりにアラームを使用します
- パラメーター化 SQL を使用します:
sql.exec('... WHERE id = ?', id) - コンストラクターでの処理を最小限にし、
blockConcurrencyWhile()を使用します
❌ 絶対にしないこと:
- マイグレーションなしで DO を作成する(エラーになります)
- クラスのエクスポートを忘れる(バインディングが見つかりません)
- setTimeout/setInterval を使用する(ハイバネーションが妨げられます)
- WebSocket の状態をインメモリだけに頼る(serializeAttachment を使用してください)
- マイグレーションを段階的にデプロイする(マイグレーションはアトミックです)
- 既存の KV ベースの DO で SQLite を有効にする(新しいクラスを作成する必要があります)
- ロケーションヒントが保証されると想定する(ベストエフォートのみです)
既知の問題の防止
このスキルは、15件以上の文書化された問題を防ぎます。特に重要な上位3件:
問題 #1: クラスがエクスポートされていない
エラー: "binding not found" | 理由: DO クラスがエクスポートされていません
修正: export default MyDO;
問題 #2: マイグレーションがない
エラー: "migrations required" | 理由: マイグレーションエントリーなしで DO を作成しました
修正: マイグレーションに { "tag": "v1", "new_sqlite_classes": ["MyDO"] } を追加します
問題 #3: setTimeout がハイバネーションを妨げる
エラー: DO がハイバネーションせず、料金が高くなる | 理由: setTimeout がハイバネーションを妨げます
修正: 代わりに await ctx.storage.setAlarm(Date.now() + 1000) を使用します
その他の12件の問題: マイグレーションタイプの誤り、コンストラクターの過剰な処理、インメモリ状態の消失、送信 WebSocket のハイバネーション不可、グローバルな一意性に関する誤解、deleteAll の部分的な実行、バインディングの不一致、状態サイズの超過、マイグレーションがアトミックでない、ロケーションヒントの無視、アラームの再試行失敗、fetch によるハイバネーションの阻止。
15件以上の問題すべてを網羅した完全なエラーカタログ、詳細な予防策、デバッグ手順、解決パターンについては、references/top-errors.md を読み込んでください。
設定と TypeScript
DO バインディングとマイグレーションを使用して wrangler.jsonc を設定し、適切なエクスポートを含む TypeScript 型を設定します。
以下を参照するには、references/typescript-config.md を読み込んでください:wrangler.jsonc の構造、TypeScript 型、Env インターフェース、tsconfig.json、よくある型の問題
公式ドキュメント:https://developers.cloudflare.com/durable-objects/
- State API(SQL):https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/
- WebSocket ハイバネーション:https://developers.cloudflare.com/durable-objects/best-practices/websockets/
- Alarms API:https://developers.cloudflare.com/durable-objects/api/alarms/
- マイグレーション:https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/
- ベストプラクティス:https://developers.cloudflare.com/durable-objects/best-practices/
ご質問がありますか? よくある問題については
references/top-errors.mdを読み込むか、動作する例についてはtemplates/を確認してください