Skill Featured

Implement Robust WebSocket Communication

WebSocket implementation expertise covering client/server code, auth, reconnection, scaling, and security best practices.

Works with githubwebsocket

Maintainer of this project? Claim this page to edit the listing.


79
Spark score
out of 100
Status Verified Official
Updated 7 months ago
Version 1.0.0
Models

Add to Favorites

Why it matters

Implement real-time, bidirectional communication for your applications using expert WebSocket handling. This asset provides robust client and server implementations, covering connection management, message protocols, and error recovery.

Outcomes

What it gets done

01

Develop reliable WebSocket client connections with automatic reconnection and heartbeat mechanisms.

02

Build secure and scalable WebSocket servers with authentication and message routing.

03

Design and implement structured message formats for efficient data exchange.

04

Optimize WebSocket performance for high-frequency updates and large payloads.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/vb-websocket-handler | bash

Overview

WebSocket Handler Expert

Expert WebSocket implementation guidance covering client and server code patterns for reconnection, authentication, room-based broadcasting, request-response semantics, scaling, and security. Use when building or hardening a real-time WebSocket application needing reconnection, authentication, room broadcasting, or scaling across multiple servers.

What it does

This skill provides expert guidance on WebSocket implementation, real-time communication protocols, and bidirectional client-server messaging - connection lifecycle management, message handling patterns, error recovery, authentication, scaling, and performance optimization. Core principles: handle all connection states (connecting, open, closing, closed) with proper cleanup on disconnect to prevent memory leaks, use heartbeat/ping-pong for connection health monitoring, implement reconnection with exponential backoff, and manage connection pooling for multiple simultaneous connections. Message protocol design favors a consistent JSON format with message types and IDs for request-response patterns, message queuing for offline scenarios, binary frames for performance-critical data, and support for both broadcast and targeted messaging.

When to use - and when NOT to

Use this skill when building a WebSocket client or server needing robust reconnection, authentication, room-based broadcasting, or request-response semantics over a persistent connection. A client-side WebSocketClient class demonstrates the full pattern: configurable reconnectInterval/maxReconnectAttempts/heartbeatInterval, a connect() returning a Promise that resolves on open and starts a heartbeat (send({ type: 'ping' }) on an interval) and flushes any queued messages, onmessage parsing JSON safely, onclose triggering reconnection with exponential backoff (Math.min(1000 * 2^attempts, 30000)) only when the close wasn't clean, and a send() that queues messages when not connected rather than dropping them.

Inputs and outputs

A Node.js server-side example (using the ws and jsonwebtoken packages) authenticates connections via verifyClient checking a JWT from the request, tracks clients in a Map with their user, joined rooms, and last-ping time, and routes incoming messages by type (ping -> pong, join_room, broadcast -> broadcastToRoom, private_message -> sendPrivateMessage, with an error response for unknown types). broadcastToRoom sends a JSON envelope to every client in a room except an optional excluded sender, only to clients whose socket is still OPEN. A health-check loop runs periodically, terminating any client whose last pong is older than 60 seconds and pinging the rest. The recommended message envelope carries id, type, timestamp, a data payload, and metadata (e.g. room, priority). A request-response pattern layers a requestId and a 30-second timeout on top of send(), resolving or rejecting a pending Promise when the matching response arrives.

Integrations

Scaling guidance: enforce connection limits per client/IP to prevent abuse, use sticky sessions or Redis for multi-server deployments, consider sharding WebSocket connections by room or user group, and monitor memory with proper connection cleanup. Message optimization: compress large payloads, batch high-frequency updates, use binary protocols (MessagePack, Protocol Buffers) for performance-critical paths, and cache frequently sent messages. Error-handling and recovery guidance: exponential backoff for reconnection, locally storing critical messages during disconnections, showing connection status to users, gracefully handling partial messages, and logging connection metrics. Security best practices: validate and sanitize all incoming messages, rate-limit to prevent spam/DoS, use WSS (WebSocket Secure) in production, validate origin headers against CSRF, implement proper authentication/authorization, and monitor for suspicious connection patterns. A MockWebSocket test double (tracking readyState, recording sent messages, and simulating events via registered listeners) supports unit testing WebSocket handlers without a real connection.

Who it's for

Developers building or hardening real-time WebSocket applications who need concrete client and server patterns for reconnection, authentication, room-based broadcasting, request-response semantics, scaling across multiple servers, and testable handler code.

Source README

WebSocket Handler Expert

You are an expert in WebSocket implementation, real-time communication protocols, and bidirectional client-server messaging systems. You understand connection lifecycle management, message handling patterns, error recovery, authentication, scaling considerations, and performance optimization for WebSocket applications.

Core WebSocket Principles

Connection Lifecycle Management

  • Always handle connection states: connecting, open, closing, closed
  • Implement proper cleanup on disconnect to prevent memory leaks
  • Use heartbeat/ping-pong mechanisms for connection health monitoring
  • Handle reconnection logic with exponential backoff
  • Manage connection pooling for multiple simultaneous connections

Message Protocol Design

  • Structure messages with consistent format (JSON recommended)
  • Include message types, IDs for request-response patterns
  • Implement message queuing for offline scenarios
  • Use binary frames for performance-critical data
  • Design for both broadcast and targeted messaging

Client-Side Implementation Patterns

Robust WebSocket Client

class WebSocketClient {
  constructor(url, options = {}) {
    this.url = url;
    this.options = { 
      reconnectInterval: 1000,
      maxReconnectAttempts: 5,
      heartbeatInterval: 30000,
      ...options 
    };
    this.ws = null;
    this.reconnectAttempts = 0;
    this.messageQueue = [];
    this.listeners = new Map();
    this.heartbeatTimer = null;
  }

  connect() {
    return new Promise((resolve, reject) => {
      try {
        this.ws = new WebSocket(this.url);
        
        this.ws.onopen = (event) => {
          console.log('WebSocket connected');
          this.reconnectAttempts = 0;
          this.startHeartbeat();
          this.flushMessageQueue();
          resolve(event);
        };

        this.ws.onmessage = (event) => {
          try {
            const message = JSON.parse(event.data);
            this.handleMessage(message);
          } catch (error) {
            console.error('Failed to parse message:', error);
          }
        };

        this.ws.onclose = (event) => {
          console.log('WebSocket closed:', event.code, event.reason);
          this.stopHeartbeat();
          if (!event.wasClean && this.shouldReconnect()) {
            this.reconnect();
          }
        };

        this.ws.onerror = (error) => {
          console.error('WebSocket error:', error);
          reject(error);
        };
      } catch (error) {
        reject(error);
      }
    });
  }

  send(message) {
    const payload = JSON.stringify({
      id: this.generateId(),
      timestamp: Date.now(),
      ...message
    });

    if (this.isConnected()) {
      this.ws.send(payload);
    } else {
      this.messageQueue.push(payload);
    }
  }

  isConnected() {
    return this.ws && this.ws.readyState === WebSocket.OPEN;
  }

  startHeartbeat() {
    this.heartbeatTimer = setInterval(() => {
      if (this.isConnected()) {
        this.send({ type: 'ping' });
      }
    }, this.options.heartbeatInterval);
  }

  reconnect() {
    if (this.reconnectAttempts < this.options.maxReconnectAttempts) {
      this.reconnectAttempts++;
      const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
      setTimeout(() => this.connect(), delay);
    }
  }
}

Server-Side Implementation (Node.js)

WebSocket Server with Authentication

const WebSocket = require('ws');
const jwt = require('jsonwebtoken');

class WebSocketServer {
  constructor(server, options = {}) {
    this.wss = new WebSocket.Server({
      server,
      verifyClient: this.authenticateClient.bind(this)
    });
    this.clients = new Map();
    this.rooms = new Map();
    
    this.wss.on('connection', this.handleConnection.bind(this));
  }

  authenticateClient(info) {
    const token = this.extractToken(info.req);
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      info.req.user = decoded;
      return true;
    } catch (error) {
      return false;
    }
  }

  handleConnection(ws, req) {
    const clientId = this.generateClientId();
    const client = {
      id: clientId,
      ws: ws,
      user: req.user,
      rooms: new Set(),
      lastPing: Date.now()
    };
    
    this.clients.set(clientId, client);
    
    ws.on('message', (data) => {
      try {
        const message = JSON.parse(data);
        this.handleMessage(client, message);
      } catch (error) {
        this.sendError(ws, 'Invalid message format');
      }
    });

    ws.on('close', () => {
      this.handleDisconnect(clientId);
    });

    ws.on('pong', () => {
      client.lastPing = Date.now();
    });

    // Send welcome message
    this.sendToClient(client, {
      type: 'connected',
      clientId: clientId
    });
  }

  handleMessage(client, message) {
    switch (message.type) {
      case 'ping':
        this.sendToClient(client, { type: 'pong', timestamp: Date.now() });
        break;
      case 'join_room':
        this.joinRoom(client, message.room);
        break;
      case 'broadcast':
        this.broadcastToRoom(message.room, message.data, client.id);
        break;
      case 'private_message':
        this.sendPrivateMessage(message.targetId, message.data, client.id);
        break;
      default:
        this.sendError(client.ws, `Unknown message type: ${message.type}`);
    }
  }

  broadcastToRoom(roomId, data, excludeClientId = null) {
    const room = this.rooms.get(roomId);
    if (!room) return;

    const message = JSON.stringify({
      type: 'broadcast',
      room: roomId,
      data: data,
      timestamp: Date.now()
    });

    room.forEach(clientId => {
      if (clientId !== excludeClientId) {
        const client = this.clients.get(clientId);
        if (client && client.ws.readyState === WebSocket.OPEN) {
          client.ws.send(message);
        }
      }
    });
  }

  startHealthCheck() {
    setInterval(() => {
      this.clients.forEach((client, clientId) => {
        if (Date.now() - client.lastPing > 60000) {
          client.ws.terminate();
          this.handleDisconnect(clientId);
        } else {
          client.ws.ping();
        }
      });
    }, 30000);
  }
}

Message Pattern Best Practices

Structured Message Format

// Standard message envelope
const messageEnvelope = {
  id: 'unique-message-id',
  type: 'message_type',
  timestamp: Date.now(),
  data: {
    // actual payload
  },
  metadata: {
    room: 'optional-room-id',
    priority: 'normal|high|low'
  }
};

Request-Response Pattern

// Client request with callback
class WebSocketClient {
  sendRequest(type, data) {
    return new Promise((resolve, reject) => {
      const requestId = this.generateId();
      const timeout = setTimeout(() => {
        this.pendingRequests.delete(requestId);
        reject(new Error('Request timeout'));
      }, 30000);
      
      this.pendingRequests.set(requestId, { resolve, reject, timeout });
      
      this.send({
        type: type,
        requestId: requestId,
        data: data
      });
    });
  }

  handleResponse(message) {
    const pending = this.pendingRequests.get(message.requestId);
    if (pending) {
      clearTimeout(pending.timeout);
      this.pendingRequests.delete(message.requestId);
      
      if (message.error) {
        pending.reject(new Error(message.error));
      } else {
        pending.resolve(message.data);
      }
    }
  }
}

Performance and Scaling Considerations

Connection Limits and Load Balancing

  • Implement connection limits per client/IP to prevent abuse
  • Use sticky sessions or Redis for multi-server deployments
  • Consider WebSocket sharding by room or user groups
  • Monitor memory usage and implement connection cleanup

Message Optimization

  • Use message compression for large payloads
  • Implement message batching for high-frequency updates
  • Use binary protocols (MessagePack, Protocol Buffers) for performance-critical applications
  • Cache frequently sent messages

Error Handling and Recovery

Connection Recovery Strategies

  • Implement exponential backoff for reconnections
  • Store critical messages locally during disconnections
  • Provide connection status indicators to users
  • Handle partial message scenarios gracefully
  • Log connection metrics for monitoring

Security Best Practices

  • Always validate and sanitize incoming messages
  • Implement rate limiting to prevent spam/DoS
  • Use WSS (WebSocket Secure) in production
  • Validate origin headers to prevent CSRF
  • Implement proper authentication and authorization
  • Monitor for suspicious connection patterns

Testing WebSocket Implementations

Unit Testing WebSocket Handlers

// Mock WebSocket for testing
class MockWebSocket {
  constructor() {
    this.readyState = WebSocket.OPEN;
    this.sentMessages = [];
    this.listeners = {};
  }

  send(data) {
    this.sentMessages.push(data);
  }

  addEventListener(event, callback) {
    this.listeners[event] = callback;
  }

  simulate(event, data) {
    if (this.listeners[event]) {
      this.listeners[event](data);
    }
  }
}

Always implement comprehensive logging, monitoring, and graceful degradation for production WebSocket applications.

FAQ

Common questions

Discussion

Questions & comments ยท 0

Sign In Sign in to leave a comment.