Skip to main content

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);
ParameterTypeDescription
urlstringWebSocket server URL (e.g. wss://api.example.com/ws).
optionsWebSocketOptionsOptional configuration.

WebSocketOptions

PropertyTypeDefaultDescription
reconnectbooleantrueEnable automatic reconnection on close/error.
maxReconnectAttemptsnumber5Maximum reconnection attempts before giving up.
reconnectDelayMsnumber3000Base delay for reconnect attempts (ms). Used as the unit for backoff calculations.
heartbeatIntervalMsnumber30000Interval between automatic ping messages (ms).
messageTimeoutnumber5000Timeout for request() calls (ms).
backoffStrategyBackoffStrategy"linear"Delay strategy: "linear", "exponential", or a custom (attempt) => number function.
maxReconnectDelayMsnumber30000Upper bound for computed reconnect delay (ms). Only applies to "exponential".
jitterbooleanfalseAdd Full Jitter to exponential backoff (randomises delay in [0, cap]).
heartbeatTimeoutMsnumber5000Time to wait for any message after a ping before forcing close() (ms).
schemasRecord<string, SchemaAdapter>{}Per-message-type validators. Messages that fail validation are dropped and onValidationError fires.

BackoffStrategy

type BackoffStrategy = "linear" | "exponential" | ((attempt: number) => number);
ValueDelay formula
"linear"reconnectDelayMs × attempt
"exponential"min(maxReconnectDelayMs, reconnectDelayMs × 2^(attempt−1)), optionally jittered
(attempt) => numberYour function — full control

Methods

connect()

await ws.connect();
Opens the WebSocket connection. Returns a Promise that resolves on open or rejects on immediate error.

close()

ws.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 }.
ParameterTypeDescription
typestringMessage type identifier.
dataTPayload — 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 droppedon() 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.
ParameterTypeDefaultDescription
typestringRequest message type.
dataTRequestRequest payload.
responseTypestring${type}:responseExpected 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.