Suscríbete para recibir notificaciones de nuevas publicaciones:

Durable Objects en Dynamic Workers: proporciona a cada aplicación generada por IA su propia base de datos

2026-04-13

4 min de lectura

Hace unas semanas, anunciamos Dynamic Workers, una nueva función de la plataforma Workers que te permite cargar código de Worker sobre la marcha en un entorno seguro. La API Dynamic Worker Loader proporciona esencialmente acceso directo a la primitiva básica de aislamiento de proceso en la que Workers se ha basado todo el tiempo: aislamientos, no contenedores. Los aislamientos son mucho más ligeros que los contenedores y, como tales, pueden cargarse 100 veces más rápido utilizando 1/10 de la memoria. Son tan eficientes que se pueden considerar «desechables»: los pones en marcha para ejecutar unas pocas líneas de código y luego los descartas. Como una versión segura de eval().

Dynamic Workers tienen muchos usos. En el anuncio inicial, nos centramos en cómo utilizarlos para ejecutar código generado por agentes de IA como alternativa a las llamadas a herramientas. En este caso de uso, un agente de IA realiza acciones a petición de un usuario escribiendo unas pocas líneas de código y ejecutándolas. El código es de un solo uso, está destinado a realizar una tarea una vez, y se descarta inmediatamente después de su ejecución.

Pero, ¿y si quieres que una IA genere código más persistente? ¿Qué sucede si quieres que tu IA cree una pequeña aplicación con una interfaz de usuario personalizada con la que el usuario pueda interactuar? ¿Qué pasa si quieres que esa aplicación tenga un estado de larga duración? Pero, por supuesto, sigues queriendo que se ejecute en un entorno aislado seguro.

Una forma de hacerlo sería utilizar Dynamic Workers, y simplemente proporcionar al Worker una API RPC que le dé acceso al almacenamiento. Usando enlaces, podrías proporcionar al Dynamic Worker una API que apunte a tu base de datos SQL remota (quizás respaldada por Cloudflare D1, o una base de datos Postgres a la que accedes a través de Hyperdrive, tú decides).

Pero Workers también tiene un tipo de almacenamiento único y extremadamente rápido que puede ser perfecto para este caso de uso: Durable Objects. Un Durable Object es un tipo especial de Worker que tiene un nombre único, con una instancia global por nombre. Esa instancia tiene adjunta una base de datos SQLite, que se aloja en el disco local de la máquina donde se ejecuta Durable Object. Esto hace que el acceso al almacenamiento sea increíblemente rápido: la latencia es prácticamente nula.

Quizás, entonces, lo que realmente quieres es que tu IA escriba código para un Durable Object, y luego quieres ejecutar ese código en un Dynamic Worker.

¿Pero cómo?

Esto presenta un problema extraño. Normalmente, para utilizar Durable Objects tienes que:

  1. Escribir una clase que amplíe DurableObject.

  2. Exportarlo desde el módulo principal de tu Worker.

  3. Especificar en tu configuración de Wrangler que se debe aprovisionar almacenamiento para esta clase. Esto crea un espacio de nombres de Durable Object que apunta a tu clase para gestionar las solicitudes entrantes.

  4. Declarar un enlace de espacio de nombres de Durable Object que apunte a tu espacio de nombres (o utiliza ctx.exports), y utilízalo para realizar solicitudes a tu Durable Object.

Esto no se aplica de forma natural a los Dynamic Workers. En primer lugar, está el problema evidente: el código es dinámico. Lo ejecutas sin necesidad de llamar a la API de Cloudflare. Pero el almacenamiento Durable Object tiene que configurarse a través de la API, y el espacio de nombres tiene que apuntar a una clase de implementación. No puede apuntar a tu Dynamic Worker.

Pero hay un problema mayor. Aunque pudieras configurar de alguna manera un espacio de nombres de Durable Object para que apuntara directamente a un Dynamic Worker, ¿querrías hacerlo? ¿Quieres que tu agente (o usuario) pueda crear un espacio de nombres completo lleno de Durable Objects? ¿Que pueda usar almacenamiento ilimitado repartido por todo el mundo?

Probablemente no. Probablemente quieras algo de control. Es posible que desees limitar, o al menos hacer un seguimiento, del número de objetos que crean. Tal vez quieras limitarlos a un solo objeto (lo cual probablemente sea suficiente para aplicaciones personales programadas con Vibe). Quizás quieras añadir funciones de registro y otras herramientas de observabilidad. Métricas. Facturación. Etc.

Para hacer todo esto, lo que realmente quieres es que las solicitudes a estos Durable Objects vayan a tu código primero, donde puedes hacer toda la "logística", y luego reenviar la solicitud al código del agente. Quieres escribir un supervisor que se ejecute como parte de cada Durable Object.

Solución: Durable Object Facets

Hoy lanzamos, en versión beta abierta, una función que resuelve este problema.

Durable Object Facets te permiten cargar y crear una instancia de una clase Durable Object de forma dinámica, al tiempo que le proporcionan una base de datos SQLite para su almacenamiento. Con Facets:

  • Primero, creas un espacio de nombres de Durable Object normal, que apunta a una clase que tú escribes.

  • En esa clase, cargas el código del agente como un Dynamic Worker y lo llamas.

  • El código del Dynamic Worker puede implementar una clase Durable Object directamente. Es decir, exporta literalmente una clase declarada como extends DurableObject.

  • Estás creando una instancia de esa clase como una "faceta" de tu propio Durable Object.

  • La faceta obtiene su propia base de datos SQLite, que puede utilizar a través de las API normales de almacenamiento de Durable Objects. Esta base de datos está separada de la base de datos del supervisor, pero ambas se almacenan juntas como parte del mismo Durable Object global.

Cómo funciona

A continuación, mostramos una implementación sencilla y completa de una plataforma de aplicaciones que carga y ejecuta dinámicamente una clase 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);
  }
}

En este ejemplo:

  • AppRunner es un Durable Object "normal" escrito por el desarrollador de la plataforma (tú).

  • Cada instancia de AppRunner gestiona una aplicación. Almacena el código de la aplicación y lo carga bajo demanda.

  • La propia aplicación implementa y exporta una clase Durable Object, que la plataforma espera que se denomine App.

  • AppRunner carga el código de la aplicación utilizando Dynamic Workers y, a continuación, ejecuta el código como un Durable Object Facet.

  • Cada instancia de AppRunner es un Durable Object compuesto por dos bases de datos SQLite: una que pertenece al elemento principal (AppRunner en sí) y otra que pertenece a la faceta (App). Estas bases de datos están aisladas. La aplicación no puede leer la base de datos de AppRunner, solo la suya propia.

Para ejecutar el ejemplo, copia el código anterior en un archivo worker.js, empareja con el siguiente wrangler.jsonc, y ejecútalo localmente con 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",
    },
  ],
}

Cómo empezar

Facets son una función de Dynamic Workers, disponible en versión beta desde ya para los usuarios del plan de pago Workers.

Consulta la documentación para obtener más información sobre Dynamic Workers y Facets.

Protegemos redes corporativas completas, ayudamos a los clientes a desarrollar aplicaciones web de forma eficiente, aceleramos cualquier sitio o aplicación web, prevenimos contra los ataques DDoS, mantenemos a raya a los hackers, y podemos ayudarte en tu recorrido hacia la seguridad Zero Trust.

Visita 1.1.1.1 desde cualquier dispositivo para empezar a usar nuestra aplicación gratuita y beneficiarte de una navegación más rápida y segura.

Para saber más sobre nuestra misión para ayudar a mejorar Internet, empieza aquí. Si estás buscando un nuevo rumbo profesional, consulta nuestras ofertas de empleo.
Agents WeekPlataforma para desarrolladoresDesarrolladoresAgents WeekCloudflare WorkersDurable ObjectsAlmacenamiento

Síguenos en X

Kenton Varda|@kentonvarda
Cloudflare|@cloudflare

Publicaciones relacionadas

30 de abril de 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 de abril de 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 de abril de 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. ...