Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Durable Objects  ›  Lesson

Durable Objects — One Name, One Place, One Thread

State Management 15 minA Durable Object is a single named instance of a class, reachable from any Worker, that handles its events one at a time, making it the authoritative home for one unit of state.

Durable Objects — One Name, One Place, One Thread

Back in the KV lesson, Hoplink's live click counter had nowhere to live. A global variable is per isolate; KV accepts about one write per second per key and spreads slowly. What the counter needs is a single, authoritative home that every request, from every city, talks to. That is a Durable Object.

The rule

One name, one object, one thread — reachable from anywhere.

  • One name. You ask for an object by a name you choose, such as the short code cat. The same name always leads to the same object, anywhere in the world. Different names give different objects, so cat and dog never contend with each other.
  • One object. Exactly one instance with that name exists at a time. It runs in one Cloudflare location — usually near where it was first requested.
  • One thread. The object handles its requests one event at a time, like a single shop assistant serving a queue. Your code never has two copies of itself racing to change the same field.

A Durable Object is a JavaScript class. Workers reach it through a binding and call its methods directly (remote procedure calls, or RPC):

import { DurableObject } from "cloudflare:workers";

export class LinkCounter extends DurableObject {
  count = 0;                       // lives in this object's memory

  async increment() {
    this.count += 1;
    return this.count;
  }
}

export default {
  async fetch(request, env, ctx) {
    const code = new URL(request.url).pathname.slice(1);
    const id = env.COUNTER.idFromName(code);   // name → id
    const stub = env.COUNTER.get(id);           // id → stub
    const clicks = await stub.increment();      // call it like a local method
    return new Response(`${code} has ${clicks} clicks`);
  },
};

The config declares the binding and registers the class in a migration:

{
  "durable_objects": { "bindings": [{ "name": "COUNTER", "class_name": "LinkCounter" }] },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["LinkCounter"] }]
}

One warning already: count here is only in memory. When the object goes idle, it can be evicted and count resets to 0. The persistence lesson fixes that.

The shortcut

To design with Durable Objects, ask "What is the unit of coordination?" — the smallest thing whose state must be exactly right. Make that the object's name. One object per link, per chat room, per user, per game; never one object for the whole app.

Choosing the object for Hoplink's countersStep 1 of 4
  1. Find the unit

    Each link's count must be exact; different links never affect each other. Unit: one link.

Trap: one giant object for everything. Because an object handles one event at a time, making it the single door for the whole app turns it into a bottleneck.

Drills

Answer fast — trust the rule.

quick drill

Two visitors in Paris and Sydney both open hop.example/cat. How many LinkCounter objects named cat handle them?

quick drill

What does env.COUNTER.idFromName("cat") do?

quick drill

Why is one Durable Object per link better than one for the whole site?

quick drill

A LinkCounter keeps count only in a class field. What happens after it sits idle for a while?

Remember

  • One name, one object, one thread — reachable from anywhere.
  • Shortcut: name objects by the unit of coordination, never the whole app.
  • Trap: trusting class fields to survive, and one object for everything.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainPick the object's name

Explain why Hoplink uses one Durable Object per short code for click counting, rather than a global variable, KV, or one object for the whole site.