Use this file to discover all available pages before exploring further.
Every WebSocket client that connects to a Server is represented as a Connection — a standard WebSocket augmented with an id, tags, and a small state slot. PartyServer calls lifecycle hooks on your server class at each stage of a connection’s life, giving you clean entry points for initialization, message handling, and cleanup. This page walks through those hooks and the APIs for working with the set of live connections.
Called once when the Durable Object first boots (or wakes from
hibernation). Use this to load persistent data from storage. It runs
inside ctx.blockConcurrencyWhile, so no messages are processed until it
resolves.
2
onConnect
Called when a new client completes the WebSocket handshake. Receives the
Connection and a ConnectionContext containing the originating
Request. This is the right place to initialize per-connection state.
3
onMessage
Called each time the client sends a message. Receives the Connection and
the raw WSMessage (string | ArrayBuffer | ArrayBufferView).
4
onClose
Called when the client closes the connection. Receives the connection,
WebSocket close code, reason string, and a wasClean boolean.
5
onError
Called when a transport error occurs. Receives the connection and the
error value. Does not fire for normal close events.
Each connection can hold up to 2 KB of arbitrary data in connection.state. This is stored in the Hibernation API’s per-socket attachment slot, so it survives hibernation and is available when a message arrives on a previously sleeping socket.Use connection.setState() in onConnect to attach user-specific data once the connection is established:
type UserState = { userId: string; role: "admin" | "member";};onConnect(connection: Connection<UserState>, ctx: ConnectionContext) { const url = new URL(ctx.request.url); const userId = ctx.request.headers.get("x-user-id") ?? "anonymous"; const role = url.searchParams.get("role") === "admin" ? "admin" : "member"; connection.setState({ userId, role });}
You can then read connection.state anywhere you have access to a Connection:
onMessage(connection: Connection<UserState>, message: WSMessage) { const { userId, role } = connection.state; console.log(`Message from ${role} ${userId}: ${message}`);}
Override getConnectionTags to attach one or more string labels to a connection at connect time. Tags are stored alongside the connection and can be used to filter the getConnections iterator.
getConnectionTags(connection: Connection, ctx: ConnectionContext): string[] { const url = new URL(ctx.request.url); const role = url.searchParams.get("role") ?? "member"; // Each connection supports up to 9 tags, max 256 chars each return [role];}
broadcast(message, exclude?) sends a message to every live connection. Pass an array of connection IDs in the second argument to skip those connections:
onMessage(connection: Connection, message: WSMessage) { // Send to everyone except the sender this.broadcast(message, [connection.id]);}
This example tags connections by role at connect time, stores state, and sends admin-only notices:
import { Server } from "partyserver";import type { Connection, ConnectionContext, WSMessage } from "partyserver";type UserState = { userId: string; role: "admin" | "member" };export class RoleAwareServer extends Server { // Tag each connection with the user's role getConnectionTags( connection: Connection, ctx: ConnectionContext ): string[] { const role = new URL(ctx.request.url).searchParams.get("role") ?? "member"; return [role === "admin" ? "admin" : "member"]; } onConnect(connection: Connection<UserState>, ctx: ConnectionContext) { const url = new URL(ctx.request.url); connection.setState({ userId: url.searchParams.get("userId") ?? "anon", role: (url.searchParams.get("role") as UserState["role"]) ?? "member" }); // Greet only the joining connection connection.send( JSON.stringify({ type: "welcome", role: connection.state.role }) ); } onMessage(connection: Connection<UserState>, message: WSMessage) { const { role } = connection.state; const parsed = JSON.parse(message as string) as { type: string; text?: string; }; if (parsed.type === "admin-broadcast" && role === "admin") { // Only admins can trigger a broadcast to all admins for (const admin of this.getConnections<UserState>("admin")) { admin.send( JSON.stringify({ type: "admin-notice", text: parsed.text }) ); } } else { // Regular chat: echo to everyone else this.broadcast(message, [connection.id]); } }}
Tags are set once in getConnectionTags and cannot change during a
connection’s lifetime. If you need mutable metadata, use connection.setState()
instead.