Real-time communication patterns with WebSocket, Socket.io, Server-Sent Events, and scaling strategies
64
76%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./skills/websocket-realtime/SKILL.mdimport { WebSocketServer, WebSocket } from "ws";
const wss = new WebSocketServer({ port: 8080 });
const rooms = new Map<string, Set<WebSocket>>();
wss.on("connection", (ws, req) => {
const userId = authenticateFromUrl(req.url);
if (!userId) {
ws.close(4001, "Unauthorized");
return;
}
ws.on("message", (data) => {
const message = JSON.parse(data.toString());
switch (message.type) {
case "join":
joinRoom(message.room, ws);
break;
case "leave":
leaveRoom(message.room, ws);
break;
case "broadcast":
broadcastToRoom(message.room, message.payload, ws);
break;
}
});
ws.on("close", () => {
rooms.forEach((members) => members.delete(ws));
});
ws.send(JSON.stringify({ type: "connected", userId }));
});
function joinRoom(room: string, ws: WebSocket) {
if (!rooms.has(room)) rooms.set(room, new Set());
rooms.get(room)!.add(ws);
}
function broadcastToRoom(room: string, payload: unknown, sender: WebSocket) {
const members = rooms.get(room);
if (!members) return;
const message = JSON.stringify({ type: "message", room, payload });
members.forEach((client) => {
if (client !== sender && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";
const io = new Server(httpServer, {
cors: { origin: "https://app.example.com" },
pingTimeout: 20000,
pingInterval: 25000,
});
const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));
io.use(async (socket, next) => {
const token = socket.handshake.auth.token;
try {
socket.data.user = verifyToken(token);
next();
} catch {
next(new Error("Authentication failed"));
}
});
io.on("connection", (socket) => {
socket.join(`user:${socket.data.user.id}`);
socket.on("chat:join", (roomId) => {
socket.join(`chat:${roomId}`);
socket.to(`chat:${roomId}`).emit("chat:userJoined", socket.data.user);
});
socket.on("chat:message", async ({ roomId, text }) => {
const message = await saveMessage(roomId, socket.data.user.id, text);
io.to(`chat:${roomId}`).emit("chat:message", message);
});
socket.on("disconnect", () => {
console.log(`User ${socket.data.user.id} disconnected`);
});
});app.get("/events/:userId", authenticate, (req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
const sendEvent = (event: string, data: unknown) => {
res.write(`event: ${event}\n`);
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
sendEvent("connected", { userId: req.params.userId });
const interval = setInterval(() => {
res.write(":heartbeat\n\n");
}, 30000);
const listener = (message: string) => {
const event = JSON.parse(message);
sendEvent(event.type, event.data);
};
redis.subscribe(`user:${req.params.userId}`, listener);
req.on("close", () => {
clearInterval(interval);
redis.unsubscribe(`user:${req.params.userId}`, listener);
});
});SSE is simpler than WebSocket for server-to-client unidirectional streaming. Works through HTTP proxies and load balancers without special configuration.
class ReconnectingWebSocket {
private ws: WebSocket | null = null;
private retryCount = 0;
private maxRetries = 10;
constructor(private url: string) {
this.connect();
}
private connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => { this.retryCount = 0; };
this.ws.onclose = () => { this.scheduleReconnect(); };
this.ws.onerror = () => { this.ws?.close(); };
}
private scheduleReconnect() {
if (this.retryCount >= this.maxRetries) return;
const delay = Math.min(1000 * 2 ** this.retryCount, 30000);
this.retryCount++;
setTimeout(() => this.connect(), delay);
}
send(data: string) {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(data);
}
}
}ebdf1d5
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.