Jetzt abonnieren, um Benachrichtigungen über neue Beiträge zu erhalten:

Durable Objects in Dynamic Workers: Geben Sie jeder KI-generierten App eine eigene Datenbank

2026-04-13

Lesezeit: 4 Min.

Vor einigen Wochen haben wir Dynamic Workers vorgestellt, eine neue Funktion der Workers-Plattform, mit der sich Worker-Code on-the-fly in eine sichere Sandbox laden lässt. Die Dynamic Worker Loader API bietet im Kern direkten Zugriff auf das grundlegende Isolationsprinzip, auf dem Workers von Anfang an basieren: Isolates statt Container. Isolates sind deutlich schlanker als Container und lassen sich bis zu 100-mal schneller starten bei nur einem Zehntel des Speicherbedarfs. Sie sind so effizient, dass sie als „wegwerfbar“ gelten können: Man startet sie, führt ein paar Codezeilen aus und verwirft sie anschließend wieder. Wie eine sichere Variante von eval().

Dynamic Workers haben viele Einsatzmöglichkeiten. In der ursprünglichen Ankündigung haben wir uns darauf konzentriert, wie man mit ihnen KI-Agents-generierten Code als Alternative zu Tool-Aufrufen ausführen kann. In diesem Anwendungsfall führt ein KI-Agent auf Anfrage eines Nutzers Aktionen aus, indem er einige Codezeilen schreibt und ausführt. Der Code ist für die einmalige Verwendung bestimmt, soll eine Aufgabe einmal ausführen und wird sofort nach der Ausführung wieder verworfen.

Was aber, wenn eine KI persistenteren Code generieren soll? Was, wenn Sie möchten, dass Ihre KI eine kleine Anwendung mit einer benutzerdefinierten Nutzeroberfläche erstellt, mit der der Nutzer interagieren kann? Was, wenn die Anwendung einen langlebigen Zustand haben soll? Und natürlich soll sie trotzdem weiterhin in einer sicheren Sandbox ausgeführt werden.

Eine Möglichkeit dafür wäre die Verwendung von Dynamic Workers, bei dem dem Worker einfach eine RPC-API zur Verfügung gestellt wird, die ihm Zugriff auf den Speicher ermöglicht. Mithilfe von Bindings könnten Sie dem Dynamic Worker eine API geben, die auf Ihre entfernte SQL-Datenbank verweist (vielleicht unterstützt von Cloudflare D1 oder einer Postgres-Datenbank, auf die Sie über Hyperdrive zugreifen).

Aber Workers hat auch eine einzigartige und extrem schnelle Art von Speicher, die für diesen Anwendungsfall perfekt geeignet ist: Durable Objects. Ein Durable Object ist eine spezielle Art von Worker, der einen eindeutigen Namen hat, mit einer Instanz global pro Name. Dieser Instanz ist eine SQLite-Datenbank angehängt, die sich auf der lokalen Festplatte des Rechners befindet, auf dem das Durable Object ausgeführt wird. Dadurch wird der Speicherzugriff unglaublich schnell: Es gibt praktisch keine Latenz.

Vielleicht soll Ihre KI Code für ein Durable Object schreiben, und dieser wird dann in einem Dynamic Worker ausgeführt.

Aber wie funktioniert das?

Das führt zu einem ungewöhnlichen Problem. Um Durable Objects zu verwenden, müssen Sie normalerweise:

  1. eine Klasse schreiben, die DurableObject erweitert.

  2. sie aus dem Hauptmodul Ihres Workers exportieren.

  3. in Ihrer Wrangler-Konfiguration festlegen, dass für diese Klasse Speicher bereitgestellt wird. Dadurch wird ein Durable-Object-Namespace erstellt, der auf Ihre Klasse verweist und eingehende Anfragen verarbeitet.

  4. eine Durable-Object-Namespace-Bindung deklarieren, die auf diesen Namespace verweist (oder ctx.exports verwenden), und darüber Anfragen an Ihr Durable Object stellen.

Dies gilt nicht natürlich für Dynamic Workers. Zunächst gibt es das offensichtliche Problem: Der Code ist dynamisch. Sie führen ihn aus, ohne die Cloudflare-API überhaupt aufzurufen. Der Durable-Object-Speicher muss jedoch über die API bereitgestellt werden, und der Namespace muss auf eine implementierende Klasse verweisen. Er kann nicht auf Ihren Dynamic Worker verweisen.

Doch es gibt ein grundlegenderes Problem: Selbst wenn sich ein Durable-Object-Namespace so konfigurieren ließe, dass er direkt auf einen Dynamic Worker verweist, wäre das überhaupt wünschenswert? Möchten Sie wirklich, dass Ihr Agent (oder Nutzer) einen ganzen Namespace von Durable Objects erstellen kann? Mit potenziell unbegrenztem, weltweit verteiltem Speicher?

Wahrscheinlich nicht. Vermutlich möchten Sie eine gewisse Kontrolle. Vielleicht wollen Sie die Anzahl der erstellten Objekte begrenzen oder zumindest nachverfolgen. Eventuell möchten Sie sie sogar auf ein einzelnes Objekt beschränken (für persönliche Apps mit Vibe-Code oft völlig ausreichend). Möglicherweise möchten Sie Protokollierung und weitere Beobachtbarkeitsfunktionen hinzufügen. Metriken, Abrechnung usw.

Um all dies umzusetzen, sollten Anfragen an diese Durable Objects zuerst durch Ihren Code laufen. Dort können Sie die gesamte „Logistik“ übernehmen und dann die Anfrage an den Code des Agents weiterleiten. Im Grunde schreiben Sie einen Supervisor, der in jedem Durable Object ausgeführt wird.

Die Lösung: Durable Object Facets

Heute stellen wir eine Funktion in der Open Beta vor, die dieses Problem löst.

Durable Object Facets ermöglichen es, eine Durable-Object-Klasse dynamisch zu laden und zu instanziieren, und ihr gleichzeitig eine SQLite-Datenbank für die Speicherung bereitzustellen. Mit Facets:

  • Erstellen Sie zunächst einen normalen Durable-Object-Namespace, der auf eine von Ihnen definierte Klasse verweist, die Sie schreiben.

  • Laden Sie in dieser Klasse den Code des Agents als Dynamic Worker und rufen ihn auf.

  • Kann der Code des Dynamic Workers selbst direkt eine Durable-Object-Klasse implementieren. Das heißt, der Code exportiert tatsächlich eine Klasse, die mit extends DurableObject deklariert ist.

  • Instanziieren Sie diese Klasse als „Facette“ Ihres eigenen Durable Objects.

  • Erhält jede Facette eine eigene SQLite-Datenbank, die sie über die üblichen Durable-Object-Speicher-APIs nutzen kann. Diese Datenbank ist von der des Supervisors getrennt, wird jedoch gemeinsam mit ihr als Teil desselben übergeordneten Durable Objects gespeichert.

So funktioniert’s

Hier ist eine einfache, vollständige Implementierung einer App-Plattform, die eine Durable-Object-Klasse dynamisch lädt und ausführt:

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);
  }
}

Ein Beispiel:

  • AppRunner ist ein „normales“ Durable Object, das vom Plattformentwickler (Sie) geschrieben wurde.

  • Jede Instanz von AppRunner verwaltet eine Anwendung. Sie speichert den App-Code und lädt ihn bei Bedarf.

  • Die Anwendung selbst implementiert und exportiert eine Durable-Object-Klasse, von der die Plattform erwartet, dass sie den Namen App trägt.

  • AppRunner lädt den Anwendungscode mit Dynamic Workers und führt den Code dann als Durable Object Facet aus.

  • Jede Instanz von AppRunner ist ein Durable Object, das aus zwei SQLite-Datenbanken besteht: eine, die dem übergeordneten Element (AppRunner selbst) gehört, und eine, die der Facette (App) gehört. Diese Datenbanken sind isoliert: Die Anwendung kann die Datenbank von AppRunner nicht lesen, nur ihre eigene.

Um das Beispiel auszuführen, kopieren Sie den obigen Code in eine Datei worker.js, kombinieren Sie sie mit der folgenden wrangler.jsonc, und führen Sie sie lokal mit npx wrangler dev aus.

// 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",
    },
  ],
}

Jetzt entwickeln

Facets sind ein Feature von Dynamic Workers, das für Nutzer des kostenpflichtigen Workers-Tarifs in der Beta-Phase sofort verfügbar ist.

In der Dokumentation erfahren Sie mehr über Dynamic Workers und Facets.

Wir schützen komplette Firmennetzwerke, helfen Kunden dabei, Internetanwendungen effizient zu erstellen, jede Website oder Internetanwendung zu beschleunigen, DDoS-Angriffe abzuwehren, Hacker in Schach zu halten, und unterstützen Sie bei Ihrer Umstellung auf Zero Trust.

Greifen Sie von einem beliebigen Gerät auf 1.1.1.1 zu und nutzen Sie unsere kostenlose App, die Ihr Internet schneller und sicherer macht.

Wenn Sie mehr über unsere Mission, das Internet besser zu machen, erfahren möchten, beginnen Sie hier. Sie möchten sich beruflich neu orientieren? Dann werfen Sie doch einen Blick auf unsere offenen Stellen.
Agents WeekEntwicklerplattformEntwicklerAgents WeekCloudflare WorkersDurable ObjectsSpeicherung

Folgen auf X

Kenton Varda|@kentonvarda
Cloudflare|@cloudflare

Verwandte Beiträge

30. April 2026

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. ...

22. April 2026

Making Rust Workers reliable: panic and abort recovery in wasm‑bindgen

Panics in Rust Workers were historically fatal, poisoning the entire instance. By collaborating upstream on the wasm‑bindgen project, Rust Workers now support resilient critical error recovery, including panic unwinding using WebAssembly Exception Handling....

20. April 2026

The AI engineering stack we built internally — on the platform we ship

We built our internal AI engineering stack on the same products we ship. That means 20 million requests routed through AI Gateway, 241 billion tokens processed, and inference running on Workers AI, serving more than 3,683 internal users. Here's how we did it. ...