worker.ts, declare them in __DO_MANIFEST__, and the SDK handles WebSocket upgrades, RBAC, persistence, and broadcast.
Env interface so this.env.<binding> is typed inside overrides.
BaseRoom<E>
Abstract parent of all rooms. Provides WebSocket plumbing, JWT identity parsing, and the connection lifecycle. Subclass directly only when none of the specialized rooms fit - rare in practice.
onConnect may return an augmented UserAttachment - the returned value (or the default) is serialized on the WebSocket via state.acceptWebSocket(...) and survives DO hibernation.
RecordRoom<E>
Primary data DO - backs every record collection your app declares.
YjsRoom<E>
Per-document collaborative state (Y.Text, Y.Map, Y.Array).
/ws/yjs/:docId. The DO persists the full Yjs update as a single binary blob in SQLite.
CanvasRoom<E>
Collaborative canvas - shapes and viewports.
/ws/canvas/:docId.
PresenceRoom<E>
Ephemeral peer state - cursors, typing indicators, viewports. Not persisted.
/ws/presence/:scopeId.
CronRoom<E>
Scheduled-task DO. Declare tasks in the constructor config and override onTask.
/ws/cron/:roomId (admin/monitor stream).
GameRoom<E>
Authoritative tick-based game loop DO.
/ws/game/:roomId.
JobRoom<E>
Durable background-job DO. Handlers run on the DO’s alarm with a ~15-minute wall budget per tick (chain longer jobs with ctx.continue(state)). Jobs are persisted in SQLite, survive isolate restarts, and broadcast every state change over WebSocket - clients see live progress without polling. For worked patterns, see the Background jobs guide.
onJob return value becomes job.result (must be JSON-serializable); throwing fails the job and triggers a retry if attempts < maxAttempts. There is no ctx.complete() / ctx.fail() - return or throw. Sync returns are allowed - onJob does not have to be async.
P and R narrow the job payload and result types: JobRoom<Env, MyPayload, MyResult> gives you onJob(job: Job<MyPayload>, ctx): MyResult | void | Promise<MyResult | void>. Most apps leave them as unknown and cast at the dispatch site (see the scaffold pattern below).
Scaffold pattern:
/ws/jobs/:roomId. For the worker-side enqueue helper that lets you enqueue from HTTP routes, cron handlers, or server actions (different isolate from the DO), see enqueueJob below.
enqueueJob(namespace, roomId, type, payload?, opts?)
Cross-isolate enqueue helper - use anywhere outside the JobRoom DO (HTTP routes, cron handlers, server actions, AI routes).
app:${env.APP_NAME} as roomId to hit the per-app AppJobRoom. From inside an onJob handler, call this.enqueue(...) directly instead - it skips the HTTP hop.
Audio/video rooms have no SDK DO class. Use LiveKit via the livekit/* integration endpoints instead.
The DO manifest
| Export | Type | Description |
|---|---|---|
DOManifest | type | DOManifestEntry[] - shape of __DO_MANIFEST__. |
DOManifestEntry | type | { binding: string; className: string; sqlite: boolean }. |
DOBindings<typeof __DO_MANIFEST__> | type | Derives the Env interface’s DO bindings from the manifest. |
DEFAULT_DO_MANIFEST | const | Two-entry fallback (RECORD_ROOMS + YJS_ROOMS) used when an app doesn’t export __DO_MANIFEST__. |
See also
- Architecture concepts - how DOs fit into the system
- Get started → Project structure - the DO manifest in context
- Cron guide - patterns for
CronRoom