PartyKit is fundamentally a thin, ergonomic layer over Cloudflare’s Durable Objects primitive. Understanding how Durable Objects work — and how PartyKit maps its abstractions onto them — makes it straightforward to reason about costs, consistency, and the execution model of your real-time application.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/cloudflare/partykit/llms.txt
Use this file to discover all available pages before exploring further.
Durable Objects: stateful compute at the edge
A Cloudflare Durable Object (DO) is a single-threaded, stateful JavaScript class instance that lives somewhere on Cloudflare’s global network. Unlike a stateless Worker, a Durable Object:- Holds in-memory state between requests for as long as it remains active.
- Owns a co-located SQLite database accessible through
this.ctx.storage, offering strongly consistent reads and writes without a round-trip to an external database. - Processes one concurrent request at a time via an internal input gate (though I/O can happen in parallel inside
asynccode). - Hibernates automatically when idle, evicting the in-memory instance while keeping persistent storage intact; the next incoming request cold-starts the DO transparently.
- Is globally distributed — a named DO always resolves to the same single instance worldwide, ensuring consistent shared state regardless of which Cloudflare data center the client request enters.
The room-based model
PartyServer maps the concept of a “room” directly onto a Durable Object instance. Every unique combination of(server class, room name) corresponds to exactly one running DO instance. When two clients connect to room: "game-42" they are both reaching the same instance, so broadcasting between them is a direct in-process iteration — no pub/sub bus or external broker required.
Room names are resolved via Cloudflare’s idFromName(name) API, which deterministically produces the same Durable Object ID for a given name string. This means:
- Rooms are created lazily — no setup step is needed before the first client connects.
- The same room name always maps to the same DO instance, everywhere on the network.
this.nameinside aServersubclass always returns the room name that was used to address the instance.
Request routing with routePartykitRequest
routePartykitRequest is the glue between your Worker’s fetch handler and your Server subclasses. It inspects the incoming request URL for the pattern:
prefix defaults to "parties", :server is the kebab-cased Durable Object binding name, and :name is the room name. For example:
MyServer Durable Object instance named "game-42".
The function performs this mapping by iterating over all DO namespace bindings in env, kebab-casing their keys, and matching against the :server segment. If a match is found, it:
- Resolves the DO instance via
idFromName(name). - Forwards the request (including WebSocket upgrade headers) to that instance’s
fetchmethod. - Returns
nullfor unmatched paths, letting your fallback handler respond normally.
routePartykitRequest also accepts an options object with onBeforeConnect and onBeforeRequest hooks for request interception (e.g. authentication), a cors option for automatic CORS header injection, and routingRetry for resilience against transient Durable Object infrastructure errors.
Connection lifecycle
When a WebSocket client connects to a room, PartyServer drives it through a well-defined lifecycle. Each stage is an overridable method on yourServer class:
| Hook | When it fires |
|---|---|
onStart(props?) | Once, when the DO first starts or resumes from hibernation. Use for storage initialization or remote data fetching. |
onConnect(connection, ctx) | Each time a new WebSocket connection is established. ctx.request holds the original HTTP upgrade request. |
onMessage(connection, message) | Each time a message arrives from a connected client. |
onClose(connection, code, reason, wasClean) | When a client closes its connection. The socket is already closed; use this for cleanup only. |
onError(connection, error) | When a transport error occurs on a connection. |
onRequest(request) | When a plain HTTP (non-WebSocket) request is made to the same DO. |
onAlarm() | When a Durable Object alarm fires. Schedule alarms with this.ctx.storage.setAlarm(date). |
getConnectionTags(connection, ctx) | Returns an array of string tags for a connection at connect time. Tags can be used to filter connections via getConnections(tag). Each connection supports up to 9 tags, max 256 characters each. |
async. PartyServer awaits async hooks before processing the next event.
Connection object
Theconnection argument passed to onConnect, onMessage, onClose, and onError is a standard WebSocket extended with:
connection.id— a unique identifier for this connection (generated viananoidif not supplied by the client via?_pk=query param).connection.server— the room name this connection belongs to.connection.state— up to 2 KB of arbitrary JSON state, readable and writable viaconnection.setState(). State survives hibernation cycles.connection.tags— an array of string tags assigned bygetConnectionTags.
Hibernation
Cloudflare’s WebSocket Hibernation API allows a Durable Object to suspend its in-memory state while keeping WebSocket connections alive at the infrastructure level. When a message arrives, the runtime cold-starts the DO, re-runsonStart, and delivers the message to onMessage — all transparently.
Without hibernation, a DO with open WebSocket connections stays resident in memory indefinitely, accumulating wall-clock duration charges for every idle second. With hibernation, the DO is only charged for the time it is actually executing code.
Hibernation is opt-in. It is disabled by default. Enable it by setting a static Subclasses inherit the nearest explicitly configured
options property on your Server subclass:hibernate value from their prototype chain — you only need to set it once on a base class.webSocketMessage, webSocketClose, and webSocketError entry points rather than keeping event listeners attached in memory. Your lifecycle hooks (onMessage, onClose, onError) are called identically in both modes, so you can enable or disable hibernation without changing application logic.
The actor model analogy
Durable Objects are conceptually similar to the actor model familiar from Erlang/Elixir and the GenServer abstraction. Each Durable Object instance is an isolated “actor” that:- Processes messages sequentially (no concurrent mutation of shared state).
- Maintains its own private state.
- Communicates with the outside world via requests (analogous to message passing).
terminate callback, because a Durable Object can be evicted by the runtime at any time without a deterministic shutdown signal. Use onAlarm to schedule deferred cleanup work instead.