Server WebSocket
ServerWebSocketActor runs a server. Clients connect from
the outside; the actor accepts each connection, spawns a
per-connection child handler, and routes inbound messages there.
import { ActorSystem, Props, ServerWebSocketActor, Actor } from 'actor-ts';
class Connection extends Actor<WsConnInbound> { constructor(public readonly id: string) { super(); }
override onReceive(msg: WsConnInbound): void { if (msg.kind === 'opened') this.log.info(`connection opened: ${this.id}`); if (msg.kind === 'text') this.log.info(`from ${this.id}: ${msg.payload}`); if (msg.kind === 'closed') this.log.info(`connection closed: ${this.id}`); }}
const server = system.actorOf( Props.create(() => new ServerWebSocketActor({ host: '0.0.0.0', port: 8080, onConnection: (id) => Props.create(() => new Connection(id)), })), 'ws-server',);Each incoming WS connection spawns a Connection child. Messages
on that connection arrive as WsConnInbound; the framework
death-watches the connection actor so its termination closes the
underlying socket.
Settings
Section titled “Settings”interface ServerWebSocketActorSettings extends BrokerCommonSettings { host: string; port: number; onConnection: (id: string) => Props<WsConnInbound>; backend?: 'bun' | 'ws' | 'fastify'; // default auto-detect path?: string; // restrict to specific path tls?: TlsOptions;}onConnection is called for each new connection. Return a
Props for the per-connection actor — usually a class that
holds the connection’s id + state.
Backend choice
Section titled “Backend choice”{ backend: 'bun' } // Bun's built-in WS server{ backend: 'ws' } // Node 'ws' package (default for Node){ backend: 'fastify' } // attached to an existing Fastify server| Backend | Runtime fit |
|---|---|
'bun' | Bun-only. Uses Bun.serve’s native WebSocket. |
'ws' | Node + Bun. Uses the ws npm package. |
'fastify' | Sharing a port with an HTTP server, via @fastify/websocket. |
Auto-detection picks 'bun' on Bun, 'ws' on Node.
Sending to a specific connection
Section titled “Sending to a specific connection”// Per-connection actor sends back via its parent:override onReceive(msg: WsConnInbound) { if (msg.kind === 'text') { msg.respond('echo: ' + msg.payload); // or: msg.respondBinary(uint8array); }}The framework attaches respond(text) / respondBinary(bytes) /
close() methods to each inbound message. Easy
request-response within a single connection.
For broadcast (send to every connected client), the parent actor tracks the child set:
// Inside the server actor:this.broadcastText('event: ' + JSON.stringify({ ... }));The framework’s broadcast* methods iterate every active
connection.
Path routing
Section titled “Path routing”new ServerWebSocketActor({ host: '0.0.0.0', port: 8080, path: '/feed', // only accept connections at /feed onConnection: ...,});For multiple endpoints on the same port, spawn multiple
ServerWebSocketActors with different paths, or run a single
one and route inside onConnection based on the request URL.
Peer dependency
Section titled “Peer dependency”npm install ws # for backend: 'ws'# Bun has built-in WebSocket supportWhen to use it
Section titled “When to use it”Three good fits:
- Real-time UIs — pushing updates to browser clients.
- Custom messaging protocols — game servers, chat backends.
- Webhooks-over-WS — replacing polling with push.
For one-way streams from server to client, SSE is simpler. For request/reply RPC, HTTP (with WS for upgrade) is the norm.
Where to next
Section titled “Where to next”- I/O overview — the bigger picture.
- WebSocket (client) — for outbound connections.
- SSE — server-sent events alternative.
- HTTP overview — for regular HTTP request/response.