Durable Object facets
A facet is a Durable Object whose class comes from code loaded at run time, attached to a parent
object: this.ctx.facets.get(name, () => ({ class })). Each facet has its own SQLite database and
is stored alongside its parent, as celld stores it.
{
"name": "facets",
"main": "index.js",
"compatibility_date": "2026-01-01",
"worker_loaders": [{ "binding": "LOADER" }],
"durable_objects": {
"bindings": [{ "name": "SUPERVISOR", "class_name": "Supervisor" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["Supervisor"] }]
}
import { DurableObject, WorkerEntrypoint } from "cloudflare:workers";
// The code of the facet. A real application would load it from a bundle,
// a database, or a request; here it is a string so the example is one file.
const CODE = `
import { DurableObject } from "cloudflare:workers";
export class App extends DurableObject {
async fetch() {
const n = this.ctx.storage.kv.get("n") ?? 0;
this.ctx.storage.kv.put("n", n + 1);
const greeting = await this.env.GREETER.greet("facet");
return new Response(String(n) + ":" + greeting);
}
}
`;
export class Greeter extends WorkerEntrypoint {
greet(name) {
return `${this.ctx.props.greeting}, ${name}`;
}
}
export class Supervisor extends DurableObject {
fetch(request) {
const worker = this.env.LOADER.get("app-v1", () => ({
compatibilityDate: "2025-01-01",
mainModule: "app.js",
modules: { "app.js": CODE },
env: {
GREETER: this.ctx.exports.Greeter({
props: { greeting: "hello" },
}),
},
}));
const facet = this.ctx.facets.get("app", () => ({
class: worker.getDurableObjectClass("App"),
}));
return facet.fetch(request);
}
}
export default {
fetch(request, env) {
return env.SUPERVISOR.getByName("only").fetch(request);
},
};
This is celld’s facets example (conformance/celld-examples/facets), copied
unchanged and run by the conformance suite on Node and in Chromium.
A facet runs with its loaded Worker and is stored with its parent
- A facet of loaded code runs inside that loaded Worker’s realm (Dynamic Workers), with its own SQLite.
- Its commits are logged through the parent’s storage: a database and an LTX stream per facet, at
cells/<root>/facets/<SHA-256(name)[0..16] hex>/ltx/…, under the root’s epoch, as on celld. - When the facet opens, under the root’s lease, its database is rebuilt from that storage, as the root’s is; its rows are rescoped to the current run.
How it is checked
celld’s facets example and CellB’s facets-api project, on Node and in Chromium.