Skip to content
GitHubXDiscordRSS

Durable Object

Create, bind, and use Cloudflare Durable Objects in your Worker applications. Learn how to implement stateful microservices with persistent storage in Cloudflare Workers.

This guide explains how to create, bind and use Cloudflare Durable Objects within your Worker scripts.

  1. Create a Durable Object

    At a bare minimum, you need to create a DurableObjectNamespace object as a stable reference to your Durable Object namespace.

    import { DurableObjectNamespace } from "alchemy/cloudflare";
    const counter = DurableObjectNamespace("counter", {
    className: "Counter",
    // whether you want a sqllite db per DO (usually yes!)
    sqlite: true,
    });
  2. Bind to a Worker

    Then bind it to your Worker:

    export const worker = await Worker("Worker", {
    name: "my-worker",
    entrypoint: "./index.ts"
    bindings: {
    // bind the Durable Object namespace to your Worker
    COUNTER: counter,
    },
    });
  3. Implement the Durable Object Class

    To use this Durable Object, our Worker script must include a class for the Durable Object and then some code in the fetch handler to interact with it.

    import type { worker } from "./alchemy.run";
    import { DurableObject } from "cloudflare:workers";
    export class Counter extends DurableObject {
    declare env: typeof worker.Env;
    private count: number;
    constructor(ctx: DurableObjectState, env: typeof worker.Env) {
    super(ctx, env);
    // Initialize count from storage or 0
    this.count = Number(this.ctx.storage.kv.get('count') || 0);
    }
    async fetch(request: Request) {
    const url = new URL(request.url);
    const path = url.pathname;
    if (path === "/increment") {
    this.count++;
    } else if (path === "/decrement") {
    this.count--;
    }
    // Update count in storage
    this.ctx.storage.kv.put('count', this.count.toString());
    return Response.json({ count: this.count });
    }
    }
  4. Call from a Worker

    Now, our fetch handler can get a Durable Object instance via the COUNTER binding:

    import { env } from "cloudflare:workers";
    export default {
    async fetch(request: Request) {
    const url = new URL(request.url);
    // Create an ID for the Counter (different IDs = different Counter instances)
    const id = env.COUNTER.idFromName("A");
    // Get a stub for the Counter instance
    const stub = env.COUNTER.get(id);
    // Forward the request to the Durable Object
    return stub.fetch(request);
    },
    };
  5. (Optional) Rename the Class

    Alchemy takes care of migrations automatically when you rename the class name.

    import { DurableObjectNamespace } from "alchemy/cloudflare";
    const counter = DurableObjectNamespace("counter", {
    className: "Counter",
    className: "MyCounter",
    // whether you want a sqllite db per DO (usually yes!)
    sqlite: true,
    });