新規投稿のお知らせを受信されたい方は、サブスクリプションをご登録ください:

Dynamic WorkersのDurable Objects:AI生成アプリごとに独自のデータベースを提供

2026-04-13

4分で読了
この投稿はEnglish繁體中文FrançaisDeutschItaliano한국어Español (Latinoamérica)Español简体中文でも表示されます。

数週間前、当社はWorkersプラットフォームの新機能であるDynamic Workersを発表しました。これは、Workerコードをセキュアなサンドボックスにオンザフライで読み込むことができるものです。Dynamic Worker Loader APIは、Workersが基盤としてきた基本的なコンピューティング分離プリミティブ、すなわちコンテナではなくIsolateへの直接アクセスを提供します。Isolateはコンテナよりはるかに軽量なうえメモリの使用は1/10ですが、100倍速く読み込み可能です。非常に効率的で、「使い捨て」として扱うことができます。数行のコードを実行するために起動し、その後破棄するのです。eval()のセキュアバージョンのようなものだと言えるでしょう。

Dynamic Workersには多くの用途があります。最初の発表では、ツール呼び出しの代替として、AIエージェント生成コードをどのように実行するかに焦点を当てていました。このユースケースでは、AIエージェントは数行のコードを記述して実行することにより、ユーザーのリクエストに応じてアクションを実行します。このコードは使い捨てで、1つのタスクを1回実行することを目的としており、実行後すぐに破棄されます。

しかし、AIにより持続的なコードを生成させたい場合はどうでしょうか?AIに、ユーザーが対話できるカスタムUIで小さなアプリケーションを構築させたい場合はどうですか?そのアプリケーションの状態を長期間維持させる場合はどうすればよいでしょうか?もちろん、セキュアなサンドボックスで実行したいでしょう。

それを実現する方法の1つが、Dynamic Workersを使用し、単純にストレージにアクセスできるRPC APIをWorkerに提供することです。バインディングを使用すれば、Dynamic WorkerにリモートSQLデータベース(おそらくCloudflare D1がサポートするもの、またはHyperdriveを介してアクセスするPostgresデータベースという選択肢があります)を指すAPIを与えることができます。

しかし、Workersには、このユースケースに最適な、他に類を見ない、極めて高速なストレージタイプであるDurable Objectsがあります。Durable Objectは、一意の名称を持つ特殊なWorkerで、名称ごとにグローバルに1つのインスタンスを持つことができます。このインスタンスには、Durable Objectが実行されるマシンのローカルディスク上に存在するSQLiteデータベースが接続されています。これにより、ストレージへのアクセスが驚くほど高速になり遅延は事実上ゼロです。

理想としては、AIにDurable Objectのコードを書かせ、そのコードをDynamic Workerで実行させたいということでしょう。

でも、どうやって?

これは厄介な問題を引き起こします。Durable Objects使用時の必要事項は、以下の通りです。

  1. DurableObjectを拡張するクラスの記述。

  2. Workerのメインモジュールからのエクスポート。

  3. Wrangler設定で、このクラスにストレージをプロビジョニングするよう指定。これにより、着信リクエストを処理するクラスを指すDurable Object名前空間が作成される。

  4. 自身の名前空間を指す Durable Object 名前空間バインディングを宣言し(または ctx.exports を使用し)、これを使用してDurable Objectへのリクエストを実行。

これは、Dynamic Workersには自然には拡張されません。まず、明らかな問題があります。コードが動的であるということです。Cloudflare APIをまったく呼び出さずに実行できます。ただし、Durable ObjectストレージはAPIを介してプロビジョニングする必要があり、名前空間は実装クラスを指している必要があります。Dynamic Workerを指すことはできません。

しかし、さらに深い問題があります。何らかの方法でDurable Objectの名前空間を設定してDynamic Workerを直接指すようにすることができたとしても、それを行いたいと思うでしょうか?エージェント(またはユーザー)がDurable Objectsで完全な名前空間全体を作成できるようにしたいですか?世界中に広がる無制限のストレージを使用したいですか?

おそらく、そうではないでしょう。何らかの制御が必要になるでしょう。作成するオブジェクトの数を制限または少なくとも追跡したい場合もあるでしょう。1つのオブジェクトだけに制限したいかもしれません(バイブコード化された個人アプリには十分かもしれません)。ロギングや可観測性の追加が必要になるかもしれません。指標。課金。その他。

実際、これらすべてを行うために望むのは、これらのDurable Objectsへのリクエストが、自分自身のコードにまず送られることであり、そこで「ロジスティクス」をすべて行い、その後エージェントのコードにリクエストを転送することです。すべてのDurable Objectの一部として実行されるスーパーバイザーの作成をお望みだと思います。

ソリューション:Durable Object Facets

本日、この問題を解決する機能をオープンベータ版でリリースします。

Durable Object Facetsを使用すると、Durable Objectクラスを動的に読み込んでインスタンス化し、ストレージに使用するSQLiteデータベースの提供が可能になります。Facetsでできること:

  • まず、通常のDurable Object名前空間を作成し、ご自身で作成するクラスを指定します。

  • そのクラスでは、エージェントのコードをDynamic Workerとして読み込み、呼び出します。

  • Dynamic Workerのコードは、Durable Objectクラスを直接実装できます。つまり、extends DurableObjectとして宣言されたクラスを文字通りエクスポートします。

  • そのクラスを、自分のDurable Objectの「ファセット」としてインスタンス化しています。

  • このファセットは独自のSQLiteデータベースを取得し、通常のDurable ObjectストレージAPIを介して使用することができます。そしてこのデータベースは、スーパーバイザーのデータベースとは別のものですが、この2つは同じ全体的な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です。

  • AppRunnerの各インスタンスは、1つのアプリケーションを管理します。アプリのコードを保存し、オンデマンドで読み込みます。

  • アプリケーション自体はDurable Objectクラスを実装およびエクスポートします。プラットフォームは、そのクラスがAppという名称であることを想定しています。

  • AppRunnerは、Dynamic Workersを使用してアプリケーションコードを読み込み、Durable Object Facetとしてコードを実行します。

  • AppRunnerの各インスタンスは、2つのSQLiteデータベースで構成される1つのDurable Objectです。1つは親(AppRunner自体)に属し、もう1つはファセット(App)に属します。これらのデータベースは分離されています。アプリケーションは、AppRunnerのデータベースを読み取ることができず、それ自身のデータベースのみを読み取ることができます。

例を実行するには、上記のコードを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の機能の1つであり、Workers有料プランのユーザーにすぐにベータ版でご利用いただけます。

Dynamic WorkersFacetsの詳細については、ドキュメントをご覧ください。

Cloudflareは企業ネットワーク全体を保護し、お客様がインターネット規模のアプリケーションを効率的に構築し、あらゆるWebサイトやインターネットアプリケーションを高速化し、DDoS攻撃を退けハッカーの侵入を防ぎゼロトラスト導入を推進できるようお手伝いしています。

ご使用のデバイスから1.1.1.1 にアクセスし、インターネットを高速化し安全性を高めるCloudflareの無料アプリをご利用ください。

より良いインターネットの構築支援という当社の使命について、詳しくはこちらをご覧ください。新たなキャリアの方向性を模索中の方は、当社の求人情報をご覧ください。
Agents Week開発者プラットフォーム開発者Agents WeekCloudflare WorkersDurable Objectsストレージ

Xでフォロー

Kenton Varda|@kentonvarda
Cloudflare|@cloudflare

関連ブログ投稿

2026年4月30日

Agents can now create Cloudflare accounts, buy domains, and deploy

Starting today, agents can now be Cloudflare customers. They can create a Cloudflare account, start a paid subscription, register a domain, and get back an API token to deploy code right away. Humans can be in the loop to grant permission, but there’s no need to go to the dashboard, copy and paste API tokens, or enter credit card details. ...

2026年4月22日

Rust Workersを信頼性を高める:Wasm-bindgenでのパニックと回復を中断する

Rust Workersのパニックは以前は致命的で、インスタンス全体が汚染されていました。Rust Workersは、Wasm-bindgenプロジェクトでアップストリームと共同作業することによって、WebAssembly Integration 全体を使用したパニックからの解消を含む、回復力のある重大なエラーの復旧をサポートするようになりました。...

2026年4月20日

当社が提供するプラットフォーム上に構築した、社内向けAI技術スタック

当社は、出荷している製品と同じ製品を用いて、社内のAIエンジニアリングスタックを構築しました。これは、AI Gatewayを介してルーティングされた2,410億件のリクエスト、2,410億件のトークンを処理し、Workers AI上で推論を実行し、3,683人以上の社内ユーザーにサービスを提供することを意味します。その方法をご紹介します。 ...