Quickstart
In this guide, we will set up a reconnecting WebSocket with heartbeats (ping/pong). We will also explore its configurations
React Quickstart - if you want to use socket in a react app. you can take a look at the React Quickstart
Pre requisite
you should have a local or remote websocket server that you can connect to.
Installation
npm install @resocket/socket
Setup
Socket is mostly compatible with the standard WebSocket API. so you can just use it how you'd use a normal websocket
import { Socket } from "@resocket/socket";
// WebSocket server endpoint
const ServerUrl = "ws://localhost:8000";
/**
* create a new Reconnecting Websocket
* it will automatically reconnect if the connection drops
*/
const socket = new Socket(ServerUrl);
// Listen for messages from the WebSocket server
socket.addEventListener("message", (e) => {
console.log(e.data); // Logs the WebSocket message
});
/**
* subscribe to the connection status changes
* this is useful if you want to add connection status indicators
*/
socket.addEventListener("status", (status) => {
console.log(status); // Possible values: 'connecting', 'reconnecting', 'connected', 'closed', 'disconnected'
});
HeartBeat (Ping/PONG)
You can easily add heartbeats to your connection with a simple configuration. The example below configures a heartbeat every 30 seconds, ensuring the connection remains active and responsive.
import { Socket, type SocketConfig } from "@resocket/socket";
// WebSocket server endpoint
const ServerUrl = "ws://localhost:8000";
// WebSocket protocols (optional, use an empty array if not needed)
const SocketProtocols: string[] = [];
// Socket configuration
const config: SocketConfig = {
/**
* add heartbeats to your connection
* this will send a heartbeat (ping/pong) every 30 seconds
*/
heartbeat: 30000, // 30 seconds
};
const socket = new Socket(ServerUrl, SocketProtocols, config);
Authentication
Here's one of a few ways you can add authentication to your Websocket connection using the params
option.
params
takes a function that should return an object or a Promise that resolves to an object. this object then is added to the query param of the websocket connection url.
you can use params
as follows.
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
/**
* Whatever you return from this function will be added as an queryParam to the connection url
*
* in our case we return the token,
* ws://localhost:9000/?token=${TOKEN_VALUE_THAT_WAS_RETURNED_FROM_THIS_FUNCTION}
*/
params: async () => {
const token = await MyAuthProvider.getToken();
return {
token,
};
},
});
Connection Timeouts
you can also add custom connection timeouts for both your param
function and for the connection itself.
here's the following config
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
/**
* if your params function takes longer than
* the paramsTimeout, then it'll automatically fail
* and move to retry
*/
paramsTimeout: 10000, //10 seconds - default is 10 seconds
/**
* if your websocket connection takes longer than
* the connectionTimeout, then it'll automatically fail
* and move to retry
*
* note: connectionTimeout is started after the params function resolves and socket is starting it's connection
*/
connectionTimeout: 10000, //10 seconds - default is 10seconds
});
Closing Websocket Connection
There are many ways to close the websocket connection both from the server and the client. in this section we will look the configurations and options related to closing the websocket connection.
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
/**
* Your socket will not reconnect. if the server closes the websocket connection
* with any of the closeCodes that you provide to the config.
*
* server side - you can call something like socket.close(4000) <- this is example server code
*/
closeCodes: [4000], //example value: [4001, 4002] - defaults to []
/**
* Your socket will move to a failed state (disconnected state)
* after 10 consecutive failed attempts to connect
*/
maxRetries: 10, //default is infinity
});
/**
* Calling socket.close() will also stop your websocket connection
* the connnection will not try to reconnect if you call close.
*
* after calling .close() the connection will only reconnect when you explicitly call socket.reconnect();
*/
socket.close();
StopRetry
Sometimes you may also want to stop the retry from the param
function.
for example if the user doesn't have the correct permissions or some other business logic
you can do this by throwing a StopRetry
error.
import { Socket, StopRetry } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
params: async () => {
const hasPermissions = await MyAuthProvider.hasPermissions();
/**
* We're throwing a StopRetry error here
* this will automatically tell Socket to not reconnect
* and we will move on to failed/disconnected state
*
*/
if (!hasPermissions) throw new StopRetry("You're not authorized");
//retrurn whatever auth data or any other data you want here
return {};
},
});
/**
* You can catch the stop retry error on disconnect here
* it'll be either CloseEvent, ErrorEvent, StopRetry or undefined
*/
socket.addEventListener("disconnect", (e) => {
if (e instanceof StopRetry) toast(e.message);
});
Customize Reconnection Delays
there are many ways to customize your websocket reconnection and retries. in this section we will look at the configuration related to reconnection and retries.
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
/**
* you can set the reconnection delay grow factor config option
* this let's you control how fast the reconnection delays grow
*
* delay = minReconnectionDelay * Math.pow(reconnectionDelayGrowFactor, retryCount)
*/
reconnectionDelayGrowFactor: 1.3, //default value - 1.3
/**
* your socket will have atleast the minReconnectionDelay
* before trying to reconnect
*/
minReconnectionDelay: 2000, // Default - (1000 + Math.random() * 4000)
/**
* your socket will at max wait for maxReconectionDelay
* befoer trying to reconnect
*/
maxReconnectionDelay: 4000, // Default - 10000 (10 seconds)
});
getDelay()
sometimes you may want to add custom delays based on your application logic. you can do this with getDelay
option.
using getDelay
will override the following config methods.
minReconnectionDelay
, maxReconectionDelay
, reconnectionDelayGrowFactor
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
getDelay: () => {
//you can return your custom delay here
return Math.random() * Math.random();
},
});
Customize Heartbeat
in this section we will take a look at all the config options related to heartbeats.
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
/**
* sends a heartbeat on focus events.
* will ignore focus events if set to true
*
* note: only applicable if heartbeatInterval is set
*/
ignoreFocusEvents: true, //default false
/**
* sends a heartbeat on network offline events.
* will ignore network events if set to true
*
* note: only applicable if heartbeatInterval is set
*/
ignoreFocusEvents: true, //default false
/**
* maximum number of consecutive missed ping messages
* before the connection is moved to reconnect
*
* note: only applicable if heartbeatInterval is set
*/
maxMissedPings: 2, //default 1
/**
* the amount of time to wait for server to respond to the ping message
*
* note: only applicable if heartbeatInterval is set
*/
pingTimeout: 2000, //default 3000 (3 seconds)
/**
* the ping message to send to the server
*
* note: only applicable if heartbeatInterval is set
*/
pingMessage: "PING", //default "ping"
/**
* the pong message to recieve from the server
*
* note: only applicable if heartbeatInterval is set
*/
pingMessage: "PING", //default "pong"
});
Buffering
Socket has default support for buffering. and exposes options to make it easier to add custom buffering for your application.
config 1
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
/**
* this buffers the message if the connection is dropped
* and sends all the messages upon reconnection
*/
buffering: true,
});
config2
import { Socket } from "@resocket/socket";
const socket = new Socket("ws://localhost:9000/", [], {
/**
* this buffers the message if the connection is dropped
* and sends all the messages upon reconnection
*
* this will drop the message from buffering if the messages increase the
* maxEnqueuedMessages threshold
*/
buffering: { maxEnqueuedMessages: 100 },
});
custom buffering
import { Socket } from "@resocket/socket";
let buffer = [];
const socket = new Socket("ws://localhost:9000/");
/**
* send buffered message on connect
*/
socket.addEventListener("open", () => {
buffer.map((message) => {
socket.send(message);
});
buffer = [];
});
/**
* buffer the message if socket is not ready
*/
if (socket.canSend()) {
socket.send("message");
} else {
buffer.push("message");
}
LostConnection Toast
this option allows you to handle situations where the connection is lost and does not reconnect within a specified time frame.
You can use this hook to notify users of connection issues, such as through toast notifications. This hook is triggered with the following events:
"lost"
: When the connection is lost and reconnection attempts are ongoing."restored"
: When the connection is successfully reestablished."failed"
: When the connection cannot be restored (rare).
You can configure the timeout for the lostConnectionTimeout option (default is 5 seconds):
import { Socket } from "@resocket/socket";
import { toast } from "some-toast-library"; //add your toast library here
const socket = new Socket("ws://localhost:9000/", [], {
/**
* sets the lost connection timeout
*/
lostConnectionTimeout: 10000, //defaul to 5 seconds
});
socket.addEventListener("lostConnection", (event) => {
switch (event) {
case "lost":
toast.warn("Still trying to reconnect...");
break;
case "restored":
toast.success("Successfully reconnected again!");
break;
case "failed":
toast.error("Could not restore the connection");
break;
}
});
API
Options
type SocketOptions = {
polyfills?: { WebSocket: any }; // Add a custom polyfill for Websockt
//retries related,
maxReconnectionDelay?: number; //maximum reconnection delay
minReconnectionDelay?: number; //minimum reconnection delay
reconnectionDelayGrowFactor?: number; //how fast the reconnection delay grows
//a custom delay that will override the above config if provide this argument, useful for more customized delays
getDelay?: (retryCount: number) => number;
maxRetries?: number; //maximum number of consecutive failed attempts before moving to disconnected state
//connection related
connectionTimeout?: number; // retry if not connected after this time, in ms
paramsTimeout?: number; // retry if params function is not resolved within this time, in ms
//application related
startClosed?: boolean; // start the socket in 'closed' state. and call .reconnect() to connect
lostConnectionTimeout?: number; // timeout for lostconnection event
closeCodes?: number | number[]; //close the connection if server closed with these closeCodes
buffer?: boolean | { maxEnqueuedMessages: number }; //buffering related. see the buffering section for more details
// heartbeat related
heartbeatInterval?: number; //the interval at which we send the heartbeat
maxMissedPingss?: number; //max number of missed consecutive ping messages before moving to reconnect
ignoreFocusEvents?: boolean; //will not send heartbeats on focus event. if set to true
ignoreNetworkEvents?: boolean; //will not send heartbeats on network offline event. if set to true
pingTimeout?: number; //timeout to wait for the pong message after sending the ping
pingMessage?: string; //the ping message to send to the server
pongMessage?: string; //the pong message to recieve from the server
//debug
debug?: boolean; //set to true. to see the internal logs for debugging
debugLogger?: (...args: any[]) => void; //provide a custom logger
//custom function for dynamic url
url?: (info: {
retryInfo: RetryInfo;
url: string | URL;
params: any;
}) => string;
//function for adding auth or other data
params?: (info: RetryInfo) => Promise<object>;
//do not use this~
//todo maybe mark this as experimental? - or hide behind advanced config :/
unstable_connectionResolver?: (
con: WebSocket,
resolver: () => void,
rejecter: (err?: any) => void
) => Promise<void> | void;
};
Methods
constructor(url: string, protocols: string[] | undefined, options: SocketOptions)
close()
reconnect()
getStatus(): Status
send(data: string | ArrayBuffer | Blob | ArrayBufferView)
addEventListener(type: 'open' | 'close' | 'status' | 'message' | 'error' | 'disconnect', listener: Listener)
removeEventListener(type: 'open' | 'close' | 'status' | 'message' | 'error' | 'disconnect', listener: Listener)
Attributes
binaryType: BinaryType;
bufferdAmount: number;
extensions: string;
onclose: EventListener;
onerror: EventListener;
onmessage: EventListener;
onopen: EventListener;
protocol: string;
readyState: number;
url: string;
retryCount: number;
lastMessageSent: number;