WebSockets@rhythmjs/ws
API reference
Every export of @rhythmjs/ws: one class and the types around it.
On this page
class RhythmWs#
new RhythmWs(options?: RhythmWsOptions);
route<Data extends object = WsParams>(path: string, route: WsRoute<Data>): this;
use(fn: WsMiddleware): this;
middleware(): WsMiddleware;
get routes(): readonly string[];
upgrade: (request: Request, server: Server) => Promise<Response | undefined> | null;
get websocket(): Bun.WebSocketHandler<never>;route(path, handlers)#- Registers an endpoint.
Datais this route'sws.datatype; without anupgrademember it defaults to the route params. ThrowsTypeErrorfor a non-object handlers argument. use(fn)#- Adds middleware, the router's convention:
(ctx, next)overRhythmWsContext. Middleware and routes interleave in registration order and never run for unmatched paths. Mounting another instance isuse(child.middleware()). ThrowsTypeErrorfor a non-function argument. middleware()#- Compiles the instance into one mountable
WsMiddleware, snapshotting its middleware and routes. A parent that mounts it gains the child's routes for matching, while the child's middleware stays scoped to its own pipeline. The child declares its own full prefix. routes#- The route paths this instance serves — its own, prefix applied, plus mounted children's — in registration order.
upgrade(request, server)#- The fetch-side half, bound as an arrow property. Returns
nullsynchronously when the request has noupgrade: websocketheader or matches no route; otherwise resolves toundefinedonceserver.upgrade()succeeds, the rejectingResponseset by middleware or returned by the route'supgrade,403 Forbiddenwhen the chain ends without upgrading (fail-closed), or500 Upgrade failedwhen Bun refuses the socket. websocket#- The single behavior object for
Bun.serve, spreading the constructor'sWsBehaviortuning and dispatching every event to the connection's route via thews.dataidentity. One behavior serves connections upgraded by any instance.
interface WsRoute<Data>#
interface WsRoute<Data extends object = WsParams> {
upgrade?(request: Request, params: WsParams, server: Server): Data | Response | Promise<Data | Response>;
headers?: Bun.HeadersInit | ((request: Request, params: WsParams) => Bun.HeadersInit | Promise<Bun.HeadersInit>);
open?(ws: Bun.ServerWebSocket<Data>): void | Promise<void>;
message?(ws: Bun.ServerWebSocket<Data>, message: string | Buffer): void | Promise<void>;
drain?(ws: Bun.ServerWebSocket<Data>): void | Promise<void>;
close?(ws: Bun.ServerWebSocket<Data>, code: number, reason: string): void | Promise<void>;
}Every lifecycle member is exactly Bun's WebSocketHandler member: ws is Bun's ServerWebSocket with send, subscribe, publish, cork, backpressure-aware return codes, and ws.data typed as Data. upgrade runs before server.upgrade(): return the connection's data, or a Response to reject the handshake. headers contributes to the 101 response, statically or computed per request.
RhythmWsContext and WsMiddleware#
interface RhythmWsContext {
readonly request: Request;
readonly server: Server;
response: Response | undefined;
}
type WsMiddleware = Middleware<RhythmWsContext>; // (ctx, next) from @rhythmjs/rhythmThe upgrade pipeline's context. Middleware sets ctx.response to reject the handshake and calls next() to continue; a set response always wins over a later upgrade attempt, and a chain that ends without upgrading answers 403 — fail-closed either way.
WsBehavior and RhythmWsOptions#
interface WsBehavior {
maxPayloadLength?: number;
idleTimeout?: number;
backpressureLimit?: number;
closeOnBackpressureLimit?: boolean;
sendPings?: boolean;
publishToSelf?: boolean;
perMessageDeflate?: Bun.WebSocketHandler<never>["perMessageDeflate"];
}
type WsOrigin =
| "same-origin"
| readonly string[]
| ((origin: string, request: Request) => boolean | Promise<boolean>)
| false;
interface RhythmWsOptions extends WsBehavior {
prefix?: string;
origin?: WsOrigin;
}WsBehavior is Bun's websocket tuning, passed through to Bun.serve's behavior object verbatim by the websocket getter. prefix is prepended to every path this instance registers, and to merged children's paths.
origin validates the handshake's Origin header before middleware runs, guarding against cross-site WebSocket hijacking (browsers attach cookies to handshakes, and WebSocket is exempt from CORS). The default "same-origin" requires a present Origin to match the request's own Host and answers 403 otherwise; requests without an Origin (non-browser clients) pass. An array allows exactly those origins, a predicate decides per request, and false disables the check.
Supporting types#
type WsParams = Readonly<Record<string, string>>;
type Server = Bun.Server<unknown>;