Skip to content

Realtime (WebSocket)

@delightstack/websocket is a realtime layer for SvelteKit on Cloudflare Workers — room-scoped connections, reactive presence, typed custom events, and database sync, backed by one Durable Object per room with hibernation support.

  • Room-scoped — one Durable Object per room/org; connections in a room share an instance for broadcast and presence.
  • Hibernation API — the DO sleeps between messages and recovers session state on wake.
  • SharedWorker multiplexing — a single SharedWorker holds one WebSocket per room across all tabs.
  • Reactive presencews.sessions is a Svelte 5 $state array, always current.
  • Typed events & metadata — pass a custom event map and session-metadata type as generics.
  • Auto-reconnect — exponential backoff with jitter and client ping keep-alive.
  • Database syncws.databaseHooks() wires entity changes into DatabaseClient.
Terminal window
pnpm add @delightstack/websocket
ImportUse
@delightstack/websocket/workerWebsocketServer — the Durable Object class
@delightstack/websocket/servercreateWebsocketHandle — SvelteKit upgrade handle
@delightstack/websocket/clientWebsocketClient — reactive Svelte 5 client
import { WebsocketServer } from '@delightstack/websocket/worker';
export class AppWebsocketServer extends WebsocketServer {
// optional: onConnect / onDisconnect / onMessage hooks
}

Declare it in wrangler.toml (see Architecture).

hooks.server.ts
import { createWebsocketHandle } from '@delightstack/websocket/server';
export const websocketHandle = createWebsocketHandle({
getServer: (event) => event.platform.env.WS,
// authorizes via @delightstack/auth locals by default
});

The handle intercepts /api/websocket upgrade requests, authorizes them, and forwards to the DO for the connecting room.

<script>
import { WebsocketClient } from '@delightstack/websocket/client';
const ws = new WebsocketClient();
await ws.connect(org_id);
ws.on('session:connected', (msg) => console.log(msg.meta?.user_name));
</script>
<p>{ws.sessions.length} online</p>
{#each ws.sessions as session}
<span>{session.meta?.user_name}</span>
{/each}

ws.sessions updates automatically from session:list, session:connected, and session:disconnected events.