OpenAICloudflare2026/04/13 13:08

Durable Objects in Dynamic Workers: Give each AI-generated app its own database

要点だけを先に読めるように短く再構成したセクションです。

元記事

Quick Digest

要約

要点だけを先に読めるように短く再構成したセクションです。

openaijamodel: gpt-5-mini-2025-08-07

Dynamic WorkersのFacetsで各AIアプリに専用DBを提供

Key Points

  • 動的FacetでDurable Objects実行
  • 各アプリに独立したSQLite
  • 親で制御・監視可能

Summary

Durable Object Facets(公開ベータ)は、Dynamic Workers内で動的にDurable Objectクラスを読み込み、各アプリ(facet)に専用のSQLiteバックエンドを割り当てる機能です。プラットフォーム側のDurable Object(監督者)を経由して動的コードをロードし、リクエストを転送することで、サンドボックス性、低レイテンシー、監査・課金・作成数の制御を両立できます。

Key Points

  • プラットフォーム側で通常のDurable Object(例: AppRunner)を実装し、this.ctx.facets.get(name, loaderCallback) でfacetを取得/起動する。
  • 動的コードはDynamic Workerとしてロードし、export class Xxx extends DurableObject のようにDurable Objectクラスをエクスポートすることでfacetとして実行される。
  • 各facetは親とは別のSQLiteベースのストレージ(storage.kv / storage.sql)を持ち、データは分離される(同一Durable Object内で管理・保存される)。
  • 親側でcodeIdやコード保存、LOADER.get を使った実行制御、ネットワーク無効化、ログ/メトリクス/課金のフックを実装してリソースを制限・観測する。
  • 実践的注意点:wrangler.jsoncでmigrationsにDurable Objectクラスを宣言し、worker_loadersバインディングを設定。互換日(compatibility_date)を揃える。ベータはWorkers Paidプラン向け。
  • 利点:ほぼゼロレイテンシのローカルSQLiteアクセス、使い捨てでなく長期状態保持できるDurable Objectの柔軟性、AI生成アプリごとの分離とプラットフォーム側の統制。

Quick example (手順)

  • 親Durable Objectを用意し、ユーザーアプリ用のコードを親のストレージに保存してcodeIdを生成する。
  • LOADER.get(codeId, ...)でDynamic Workerをロードし、worker.getDurableObjectClass('App')を取得する。
  • this.ctx.facets.get('app', ...)でfacetを初期化して facet.fetch(request) で転送する。

実装は上記のサンプルを参考にし、監査・課金・オブジェクト数制限のロジックを親側で必ず組み込んでください。

Full Translation

翻訳

原文の流れを保ったまま読める翻訳セクションです。

openaijamodel: gpt-5-mini-2025-08-07

Dynamic Workers の Durable Objects:各 AI 生成アプリに専用データベースを与える

Dynamic WorkersのDurable Objects:各AI生成アプリに専用データベースを与える

数週間前、Workersプラットフォームの新機能である Dynamic Workers を発表しました。これは安全なサンドボックスにWorkerコードをオンザフライで読み込める機能です。Dynamic Worker Loader API は、本来 Workers が依拠してきた基本的なコンピュート分離プリミティブ、すなわちコンテナではなく「isolates」への直接アクセスを提供します。isolates はコンテナよりはるかに軽量で、100x 速く、メモリは 1/10 で起動できます。効率が高いため、使い捨てのように扱えます:数行のコードを実行するために起動し、実行後に破棄する──secure な eval() のようなものです。

Dynamic Workers は多用途です。最初の発表では、AIエージェントが生成したコードをツール呼び出しの代替として実行する方法に焦点を当てました。このユースケースでは、AIエージェントがユーザーの要求に応じて数行のコードを書いて実行し、それは一度限りの単発処理で実行後に廃棄されます。

しかし、もし AI により永続的なコードを生成してほしい場合はどうでしょうか?ユーザーが操作できるカスタム UI を持つ小さなアプリケーションを AI に構築させたい場合は?そのアプリに長期の状態を持たせたい場合は?もちろん、依然として安全なサンドボックスで動かしたいでしょう。

一つのアプローチは Dynamic Workers を使い、Worker にストレージへの RPC API を提供することです。bindings を使えば、Dynamic Worker に Cloudflare D1 や Hyperdrive 経由でアクセスする Postgres など、リモート SQL データベースを指す API を与えられます。しかし、Workers にはこのユースケースにぴったり合う、非常に高速な独自のストレージがあります:Durable Objects です。

Durable Object は一意の名前を持ち、名前ごとにグローバルで 1 インスタンスだけ存在する特別な種類の Worker です。そのインスタンスには SQLite データベースが紐づき、Durable Object が実行されるマシンのローカルディスク上に存在します。これによりストレージアクセスは事実上ゼロレイテンシになります。

では、AI に Durable Object 用のコードを書かせ、それを Dynamic Worker で実行したい、ということになるかもしれません。ではどうすればよいでしょうか?

課題

通常、Durable Objects を使うには次の手順が必要です。

  • DurableObject を拡張するクラスを書く。
  • それを Worker のメインモジュールからエクスポートする。
  • Wrangler の設定でそのクラス用のストレージをプロビジョニングするよう指定する。
  • これにより、着信リクエストを処理するクラスを指す Durable Object namespace が作成される。
  • Durable Object namespace バインディングを宣言(または ctx.exports を使う)して、Durable Object へリクエストを送る。

これらは Dynamic Workers には自然には当てはまりません。まず明らかな問題はコードが動的であることです。Cloudflare API を呼ばずにコードを実行します。しかし Durable Object のストレージは API 経由でプロビジョニングされ、namespace は実装クラスを指している必要があり、Dynamic Worker 自体を直接指すことはできません。

さらに深い問題として、たとえ Durable Object namespace を動的 Worker に向けられるようになったとしても、それが望ましいでしょうか?エージェントやユーザーに無制限に Durable Object を作らせたいですか?世界中に分散した無制限のストレージを使わせたいですか?おそらくそうではありません。作成数の制限や追跡、あるいは一つのオブジェクトに制限する(個人向けの小さなアプリには十分)、ログや可観測性、メトリクス、課金といった制御を加えたいはずです。

これを実現するには、Durable Object へのリクエストをまずプラットフォーム側のコードで受けて "ロジスティクス"(認可、ロギング、メトリクス、課金、作成数制御など)を行い、その後エージェントのコードへ転送する必要があります。各 Durable Object にスーパーバイザーを実行させたいのです。

解決策:Durable Object Facets

本日、オープンベータとしてこの問題を解決する機能を公開します。Durable Object Facets を使うと、Durable Object クラスを動的にロードしてインスタンス化しつつ、そのクラスに SQLite データベースを提供できます。

Facets の概念は次のとおりです。

  1. 普通に Durable Object namespace を作成し、あなたが書くクラスをポイントさせます(このクラスがスーパーバイザーになります)。
  2. そのクラス内でエージェントのコードを Dynamic Worker としてロードし、呼び出します。
  3. Dynamic Worker のコードは直接 Durable Object クラスを実装してエクスポートできます(つまり、extends DurableObject と宣言されたクラスをエクスポート)。あなたはそのクラスを自分の Durable Object の "facet" としてインスタンス化します。
  4. facet には独自の SQLite データベースが与えられ、通常の Durable Object ストレージ API を通じて利用できます。このデータベースはスーパーバイザーのデータベースとは分離されていますが、同じ Durable Object の一部として一緒に保管されます。

動作例

以下は、Durable Object クラスを動的にロードして実行するアプリプラットフォームの簡単で完全な実装例です。

import { DurableObject } from "cloudflare:workers"; // For the purpose of this example, we'll use this static // application code, but in the real world this might be generated // by AI (or even, perhaps, a human user). const AGENT_CODE = ` import { DurableObject } from "cloudflare:workers"; // Simple app that remembers how many times it has been invoked // and returns it. export class App extends DurableObject { fetch(request) { // We use storage.kv here for simplicity, but storage.sql is // also available. Both are backed by SQLite. let counter = this.ctx.storage.kv.get("counter") || 0; ++counter; this.ctx.storage.kv.put("counter", counter); return new Response("You've made " + counter + " requests.\\n"); } } `; // AppRunner is a Durable Object you write that is responsible for // dynamically loading applications and delivering requests to them. // Each instance of AppRunner contains a different app. export class AppRunner extends DurableObject { async fetch(request) { // We've received an HTTP request, which we want to forward into // the app. // The app itself runs as a child facet named "app". One Durable // Object can have any number of facets (subject to storage limits) // with different names, but in this case we have only one. Call // this.ctx.facets.get() to get a stub pointing to it. let facet = this.ctx.facets.get("app", async () => { // If this callback is called, it means the facet hasn't // started yet (or has hibernated). In this callback, we can // tell the system what code we want it to load. // Load the Dynamic Worker. let worker = this.#loadDynamicWorker(); // Get the exported class we're interested in. let appClass = worker.getDurableObjectClass("App"); return { class: appClass }; }); // Forward request to the facet. // (Alternatively, you could call RPC methods here.) return await facet.fetch(request); } // RPC method that a client can call to set the dynamic code // for this app. setCode(code) { // Store the code in the AppRunner's SQLite storage. // Each unique code must have a unique ID to pass to the // Dynamic Worker Loader API, so we generate one randomly. this.ctx.storage.kv.put("codeId", crypto.randomUUID()); this.ctx.storage.kv.put("code", code); } #loadDynamicWorker() { // Use the Dynamic Worker Loader API like normal. Use get() // rather than load() since we may load the same Worker many // times. let codeId = this.ctx.storage.kv.get("codeId"); return this.env.LOADER.get(codeId, async () => { // This Worker hasn't been loaded yet. Load its code from // our own storage. let code = this.ctx.storage.kv.get("code"); return { compatibilityDate: "2026-04-01", mainModule: "worker.js", modules: { "worker.js": code }, globalOutbound: null, // block network access } }); } } // This is a simple Workers HTTP handler that uses AppRunner. export default { async fetch(req, env, ctx) { // Get the instance of AppRunner named "my-app". // (Each name has exactly one Durable Object instance in the // world.) let obj = ctx.exports.AppRunner.getByName("my-app"); // Initialize it with code. (In a real use case, you'd only // want to call this once, not on every request.) await obj.setCode(AGENT_CODE); // Forward the request to it. return await obj.fetch(req); } }

この例についてのポイント:

  • AppRunner はプラットフォーム開発者(あなた)が書く「通常の」Durable Object です。各インスタンスが 1 つのアプリケーションを管理します。アプリのコードを保存し、オンデマンドでロードします。
  • アプリ自身は Durable Object クラスを実装し、プラットフォームはそのクラス名が App であることを期待しています。
  • AppRunner は Dynamic Workers を使ってアプリコードをロードし、そのコードを Durable Object Facet として実行します。
  • 各 AppRunner インスタンスは 2 つの SQLite データベースを持つ 1 つの Durable Object で構成されます:親(AppRunner)用と facet(App)用です。これらのデータベースは分離されており、アプリは自分のデータベースのみアクセスできます。

実行方法

上記のコードを worker.js にコピーし、以下の wrangler.jsonc と組み合わせてローカルで npx wrangler dev を実行します。

// wrangler.jsonc for the above sample worker. {
  "compatibility_date": "2026-04-01",
  "main": "worker.js",
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": [
        "AppRunner"
      ]
    }
  ],
  "worker_loaders": [
    {
      "binding": "LOADER",
    },
  ],
}

利用開始

Facets は Dynamic Workers の機能で、Workers Paid プランのユーザー向けにベータで即時利用可能です。Dynamic Workers と Facets に関する詳細はドキュメントを参照してください。

Cloudflare の connectivity cloud は企業ネットワーク全体を保護し、顧客がインターネットスケールのアプリケーションを効率的に構築するのを支援し、あらゆるウェブサイトやインターネットアプリケーションを高速化し、DDoS 攻撃から守り、ハッカーの脅威を低減し、Zero Trust への道を支援します。無料のアプリを使ってインターネットをより速く安全にするには、任意のデバイスから 1.1.1.1 を訪れてください。

より良いインターネットを作るという我々のミッションの詳細はここから始めてください。キャリアの新しい方向性を探している場合は、オープンポジションもご覧ください。

Tags: server-island-start Developer Platform Developers Agents Week Cloudflare Workers Durable Objects Storage