mcpField
C
cloudflare-durable-objects

cloudflare-durable-objects

github.com/secondskyクラウド・DevOps240227 回インストール5 週間前に更新
リポジトリ

cloudflare-durable-objects について

Durable Objects を使う Cloudflare Workers 開発者向けのスキルです。永続状態や協調処理、WebSocket のハイバネーションを扱い、セットアップ、移行、デバッグ、最適化のガイドと自律型エージェントを備えています。

主な機能

  1. 01プロジェクトのひな型と設定ガイド
  2. 02SQL とキー・バリュー保存のリファレンス
  3. 03WebSocket ハイバネーションのパターン
  4. 04アラームと RPC のパターン
  5. 05移行、デバッグ、最適化のワークフロー
  6. 06自動修正とパターン実装

活用シーン

  1. 01チャットルームやセッション管理の作成
  2. 02レート制限やリーダー選出の実装
  3. 03Durable Objects の移行設定
  4. 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.md
alarms-api.md
best-practices.md
common-patterns.md
data-modeling.md
error-codes.md
gradual-deployments.md
migration-cheatsheet.md
migrations-guide.md
monitoring-debugging.md
performance-optimization.md
rpc-metadata.md
rpc-patterns.md
security-best-practices.md
state-api-reference.md
stubs-routing.md
top-errors.md
typescript-config.md
vitest-testing.md
websocket-hibernation.md
wrangler-commands.md
scripts/
check-versions.sh
migration-generator.sh
setup-vitest-do.sh
validate-do-config.sh
templates/
alarms-api-do.ts
basic-do.ts
gradual-deployment-config.jsonc
location-hints.ts
multi-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 つあります:

  1. idFromName(name) - 名前付き DO(最も一般的):グローバルに同じインスタンスへ決定的にルーティングします
  2. newUniqueId() - ランダム ID:新しい一意のインスタンスを作成します。後でアクセスできるように ID を保存する必要があります
  3. 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 種類のマイグレーション:

  1. 新しい DO の作成: new_sqlite_classes(推奨、1GB)または new_classes(従来の KV、128MB)を使用します
  2. DO の名前変更: from/to マッピングを指定して renamed_classes を使用します(データは保持され、バインディングは引き継がれます)
  3. DO の削除: deleted_classes を使用します(⚠️ 即時削除され、取り消せません。すべてのストレージが失われます)
  4. 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つのパターン:

  1. レート制限 - スライディングウィンドウによるユーザーごとのレート制限と、リクエスト追跡のための KV ストレージ
  2. セッション管理 - TTL、SQL ストレージ、アラームによる自動クリーンアップを備えたユーザーセッション
  3. リーダー選出 - SQL 制約とハートビート機構による単一リーダーの保証
  4. 複数 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/

コメント