Import
import { WebSocketHelper } from "bytekit";
What it does
WebSocketHelper wraps the native WebSocket API with automatic reconnection, configurable backoff strategies, per-type schema validation, ping/pong heartbeat monitoring, and a typed event system. It handles the full lifecycle — connect, send, validate, reconnect, and graceful disconnect — so you can focus on message handling.
Constructor
const ws = new WebSocketHelper(url, options?: WebSocketOptions);
| Parameter | Type | Description |
|---|
url | string | WebSocket server URL (e.g. wss://api.example.com/ws). |
options | WebSocketOptions | Optional configuration. |
WebSocketOptions
| Property | Type | Default | Description |
|---|
reconnect | boolean | true | Enable automatic reconnection on close/error. |
maxReconnectAttempts | number | 5 | Maximum reconnection attempts before giving up. |
reconnectDelayMs | number | 3000 | Base delay for reconnect attempts (ms). Used as the unit for backoff calculations. |
heartbeatIntervalMs | number | 30000 | Interval between automatic ping messages (ms). |
messageTimeout | number | 5000 | Timeout for request() calls (ms). |
backoffStrategy | BackoffStrategy | "linear" | Delay strategy: "linear", "exponential", or a custom (attempt) => number function. |
maxReconnectDelayMs | number | 30000 | Upper bound for computed reconnect delay (ms). Only applies to "exponential". |
jitter | boolean | false | Add Full Jitter to exponential backoff (randomises delay in [0, cap]). |
heartbeatTimeoutMs | number | 5000 | Time to wait for any message after a ping before forcing close() (ms). |
schemas | Record<string, SchemaAdapter> | {} | Per-message-type validators. Messages that fail validation are dropped and onValidationError fires. |
BackoffStrategy
type BackoffStrategy = "linear" | "exponential" | ((attempt: number) => number);
| Value | Delay formula |
|---|
"linear" | reconnectDelayMs × attempt |
"exponential" | min(maxReconnectDelayMs, reconnectDelayMs × 2^(attempt−1)), optionally jittered |
(attempt) => number | Your function — full control |
Methods
connect()
Opens the WebSocket connection. Returns a Promise that resolves on open or rejects on immediate error.
close()
Closes the connection and permanently stops reconnection. Use this for intentional disconnects.
send(type, data)
ws.send("subscribe", { channel: "orderbook" });
Sends a JSON message with the shape { type, data, timestamp }.
| Parameter | Type | Description |
|---|
type | string | Message type identifier. |
data | T | Payload — any serialisable value. |
on(type, handler)
const unsub = ws.on("trade", (data) => {
console.log("Trade received:", data);
});
// Stop listening:
unsub();
Subscribes to messages of a given type. Returns an unsubscribe function.
onError(handler)
ws.onError((error) => console.error("WS error:", error));
Subscribes to connection and handler errors. Returns an unsubscribe function.
onReconnect(handler)
const unsub = ws.onReconnect((attempt, delay) => {
console.log(`Reconnecting — attempt ${attempt} in ${delay}ms`);
});
Fires just before each reconnect delay begins. Receives the 1-based attempt number and the computed delay. Returns an unsubscribe function.
onMaxRetriesReached(handler)
ws.onMaxRetriesReached(() => {
showError("Connection lost. Please refresh.");
});
Fires once when all maxReconnectAttempts are exhausted. No further reconnection will be attempted. Returns an unsubscribe function.
onValidationError(handler)
ws.onValidationError((error, message) => {
console.warn(`Invalid message of type "${message.type}":`, error.message);
});
Fires when a message fails schema validation. The message is dropped — on() handlers are not called. Returns an unsubscribe function.
request(type, data, responseType?)
const response = await ws.request<OrderRequest, OrderConfirmation>(
"order:create",
{ symbol: "BTC-USD", qty: 1 }
);
Sends a message and waits for the matching response type (${type}:response by default). Rejects after messageTimeout ms.
| Parameter | Type | Default | Description |
|---|
type | string | — | Request message type. |
data | TRequest | — | Request payload. |
responseType | string | ${type}:response | Expected response message type. |
isConnected()
if (ws.isConnected()) {
ws.send("ping", {});
}
Returns true if the socket is in the OPEN state.
getState()
const state = ws.getState(); // WebSocket.readyState number
Returns the raw WebSocket.readyState value (0–3).
Examples
Basic connection with typed messages
import { WebSocketHelper } from "bytekit";
interface TradeEvent {
symbol: string;
price: number;
qty: number;
}
const ws = new WebSocketHelper("wss://api.example.com/ws");
await ws.connect();
ws.on<TradeEvent>("trade", (data) => {
console.log(`${data.symbol} @ ${data.price}`);
});
ws.send("subscribe", { channel: "trades" });
Exponential backoff with reconnect events
import { WebSocketHelper } from "bytekit";
const ws = new WebSocketHelper("wss://api.example.com/ws", {
backoffStrategy: "exponential",
reconnectDelayMs: 1000,
maxReconnectDelayMs: 30000,
jitter: true,
});
ws.onReconnect((attempt, delay) => {
console.log(`Reconnect attempt ${attempt} in ${delay}ms`);
});
ws.onMaxRetriesReached(() => {
showBanner("Connection lost — please refresh the page.");
});
await ws.connect();
Schema validation with Zod
import { WebSocketHelper } from "bytekit";
import { z } from "zod";
const TradeSchema = z.object({
symbol: z.string(),
price: z.number().positive(),
qty: z.number().positive(),
});
const ws = new WebSocketHelper("wss://api.example.com/ws", {
schemas: {
trade: TradeSchema, // SchemaAdapter — any object with .parse()
},
});
ws.onValidationError((error, msg) => {
console.warn(`Dropped invalid "${msg.type}" message:`, error.message);
});
ws.on("trade", (data) => {
// data is a validated TradeSchema output — safe to use
renderTrade(data);
});
await ws.connect();
Pong detection (forced close on silence)
import { WebSocketHelper } from "bytekit";
const ws = new WebSocketHelper("wss://api.example.com/ws", {
heartbeatIntervalMs: 15000, // send ping every 15s
heartbeatTimeoutMs: 5000, // force-close if no message within 5s of ping
backoffStrategy: "exponential",
});
// Any incoming message resets the pong timer automatically.
// If the server goes silent after a ping, ws.close() fires and
// the reconnect pipeline takes over.
await ws.connect();
Graceful shutdown
window.addEventListener("beforeunload", () => {
ws.close();
});
All on* subscription methods return an unsubscribe function. Store and call it when the component unmounts to avoid memory leaks.
When reconnect is true, the helper reconnects on unexpected closures. Call close() explicitly to stop reconnection and close the connection cleanly. Calling close() also clears any pending pong timeout timer.