keepalive-ws

A command server and client for simplified WebSocket communication, with built-in ping and latency messaging. Provides reliable, Promise-based communication with automatic reconnection and command queueing.
For a TCP-based, node-only solution with a similar API, see duplex.
Features
- Promise-based API - All operations return Promises for easy async/await usage
- Command queueing - Commands are automatically queued when offline
- Reliable connections - Robust error handling and reconnection
- Bidirectional communication - Full-duplex WebSocket communication
- Latency monitoring - Built-in ping/pong and latency measurement
- Room-based messaging - Group connections into rooms for targeted broadcasts
- Lightweight - Minimal dependencies
Server
import { KeepAliveServer, WSContext } from "@prsm/keepalive-ws/server";
const server = new KeepAliveServer({
port: 8080,
pingInterval: 30000,
latencyInterval: 5000,
});
server.registerCommand("echo", async (context) => {
return `Echo: ${context.payload}`;
});
server.registerCommand("throws", async () => {
throw new Error("Something went wrong");
});
server.registerCommand("join-room", async (context) => {
const { roomName } = context.payload;
await server.addToRoom(roomName, context.connection);
await server.broadcastRoom(roomName, "user-joined", {
id: context.connection.id
});
return { success: true };
});
server.registerCommand("broadcast", async (context) => {
server.broadcast("announcement", context.payload);
return { sent: true };
});
Client
import { KeepAliveClient } from "@prsm/keepalive-ws/client";
const client = new KeepAliveClient("ws://localhost:8080", {
pingTimeout: 30000,
maxLatency: 2000,
shouldReconnect: true,
reconnectInterval: 2000,
maxReconnectAttempts: Infinity,
});
await client.connect();
try {
const response = await client.command("echo", "Hello world", 5000);
console.log("Response:", response);
} catch (error) {
console.error("Error:", error);
}
await client.command("join-room", { roomName: "lobby" });
client.on("user-joined", (event) => {
console.log("User joined:", event.detail.id);
});
client.on("latency", (event) => {
console.log("Current latency:", event.detail.latency, "ms");
});
await client.close();
Extended Server API
Room Management
await server.addToRoom("roomName", connection);
await server.removeFromRoom("roomName", connection);
const roomConnections = await server.getRoom("roomName");
await server.clearRoom("roomName");
Broadcasting
server.broadcast("eventName", payload);
server.broadcast("eventName", payload, connections);
server.broadcastExclude(connection, "eventName", payload);
server.broadcastRoom("roomName", "eventName", payload);
server.broadcastRoomExclude("roomName", "eventName", payload, connection);
server.broadcastRemoteAddress(connection, "eventName", payload);
Middleware
server.globalMiddlewares.push(async (context) => {
if (!isAuthenticated(context)) {
throw new Error("Unauthorized");
}
});
server.registerCommand(
"protected-command",
async (context) => {
return "Protected data";
},
[
async (context) => {
if (!hasPermission(context)) {
throw new Error("Forbidden");
}
}
]
);
Multi-Instance Room Support
To enable multi-instance room support (so rooms are shared across all server instances), configure the server with roomBackend: "redis" and provide redisOptions:
import { KeepAliveServer } from "@prsm/keepalive-ws/server";
const server = new KeepAliveServer({
port: 8080,
roomBackend: "redis",
redisOptions: { host: "localhost", port: 6379 }
});
All room management methods become async and must be awaited.
Graceful Shutdown
await client.close();
server.close();