OpenAICloudflareApr 13, 2026, 1:08 PM

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

A condensed section focused on the key takeaways first.

Original Post

Quick Digest

Summary

A condensed section focused on the key takeaways first.

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

Durable Object Facets: per-app SQLite for Dynamic Workers

Key Points

  • Facets let Dynamic Workers export Durable Object classes at runtime
  • Each facet gets an isolated SQLite-backed Durable Object storage
  • Open beta now available for Workers Paid plan

Summary

Cloudflare now supports Durable Object Facets (beta), a way to dynamically load and run a Durable Object class inside a supervisor Durable Object using Dynamic Workers. Each facet gets its own SQLite-backed Durable Object storage (isolated from the supervisor), letting AI-generated or user-generated apps have persistent, ultra-low-latency state while the platform retains control for logging, billing, and limits.

Key Points

  • What it is

    • A supervisor Durable Object (you write) can dynamically instantiate a Durable Object class from Dynamic Worker code as a "facet."
    • Each facet receives its own SQLite database that is separate from the supervisor's database but stored together as part of the same DO instance.
  • How it works (practical flow)

    • Supervisor DO uses ctx.facets.get(name, initializer) to obtain/instantiate a facet.
    • In the initializer the supervisor loads a Dynamic Worker via the Dynamic Worker Loader API (loader.get/load) and extracts the exported Durable Object class (getDurableObjectClass).
    • The supervisor forwards requests (facet.fetch(request)) or calls RPC into the facet; the facet uses ctx.storage.kv or ctx.storage.sql (both backed by SQLite).
    • Supervisor can persist app code and codeId in its storage and pass them to the loader (example uses crypto.randomUUID()).
  • Implementation notes

    • The dynamic app must export a class extending DurableObject (e.g., export class App extends DurableObject {}).
    • Wrangler config needs a migrations entry with new_sqlite_classes for the supervisor (e.g., "AppRunner") and a worker_loaders binding for the loader (binding: "LOADER").
    • Example local dev: npx wrangler dev. Use get() instead of load() to reuse loaded workers when appropriate.
    • You can restrict network access for loaded code (e.g., globalOutbound: null) and implement logging/metrics/limits in the supervisor.
  • Caveats and availability

    • Facets are in open beta and require the Workers Paid plan.
    • The platform designer remains in control: you can limit object creation, add observability, and enforce billing/quotas rather than exposing raw namespace creation to untrusted code.

Actionable next steps

  • Build a supervisor Durable Object that: stores app code, uses loader.get(...) to load Dynamic Workers, calls worker.getDurableObjectClass("YourClass"), and exposes facet.fetch(request).
  • Update wrangler.jsonc (migrations.new_sqlite_classes and worker_loaders binding) and run npx wrangler dev for local testing.
  • Implement supervisor-side limits, logging, and network restrictions before enabling user/agent-generated code in production.

Full Translation

Translations

A translation section that keeps the flow of the original article.

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

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