Back to KB

reduce this latency to sub-100ms levels and minimize payload size to frame headers onl

Difficulty
Intermediate
Read Time
83 min

Building a Real-Time Chat App with Node.js and WebSocket

By Codcompass TeamΒ·Β·83 min read

Architecting Bidirectional Channels: A Production-Ready WebSocket Guide with Node.js

Current Situation Analysis

Real-time communication has transitioned from a differentiator to a baseline requirement in modern software. Users expect instant feedback in collaboration tools, live dashboards, and messaging platforms. Despite this, many engineering teams struggle to implement reliable bidirectional channels, often falling back on inefficient HTTP polling or deploying fragile WebSocket implementations that degrade under load.

The primary pain point is not the WebSocket protocol itself, which is standardized and efficient, but the architectural discipline required to manage persistent state. Developers frequently underestimate the complexity of session lifecycle management, room isolation, and network volatility. A naive implementation might work for a handful of concurrent users but will exhibit memory leaks, message ordering issues, or silent failures when scaled.

Data indicates that HTTP polling can consume up to 90% more bandwidth than WebSockets due to repeated header overhead, while introducing latency spikes between 1 to 5 seconds. WebSockets reduce this latency to sub-100ms levels and minimize payload size to frame headers only. However, realizing these benefits requires a robust server-side architecture that treats connections as first-class citizens with explicit lifecycle hooks, rather than ephemeral request-response cycles.

WOW Moment: Key Findings

The following comparison highlights why WebSockets are the superior choice for bidirectional real-time applications, provided the implementation addresses state management rigorously.

ProtocolLatencyBandwidth OverheadBidirectionalState Management Complexity
HTTP PollingHigh (1–5s)High (Headers per request)NoLow (Stateless)
Server-Sent EventsLowMediumNoMedium (Server-driven)
WebSocketsUltra-low (<100ms)Low (Frame headers only)YesHigh (Shared State Required)

Why this matters: WebSockets offer the lowest latency and true bidirectional communication, enabling features like typing indicators, presence updates, and instant message delivery. The trade-off is state management complexity. The architecture must explicitly track active sessions, handle disconnections gracefully, and isolate traffic by logical channels to prevent data leakage and ensure scalability.

Core Solution

This section outlines a production-grade implementation using TypeScript and the ws library. The architecture employs a Hub Pattern, encapsulating connection logic, message routing, and state management within a dedicated class. This approach promotes separation of concerns and simplifies testing.

Architecture Decisions

  1. TypeScript Integration: Enforces strict typing for payloads and session metadata, reducing runtime errors and improving developer experience.
  2. Hub Pattern: Centralizes WebSocket logic. The CommunicationHub manages the Map of active connections and exposes methods for routing.
  3. Channel Isolation: Clients connect with a channel parameter. Messages are routed only to clients within the same channel, enabling multi-tenant or room-based architectures.
  4. Explicit Lifecycle Hooks: Every connection triggers setup, message handling, teardown, and error routines, ensuring no resources are leaked.

Implementation

1. Server-Side Hub and Session Management

import { WebSocketServer, WebSocket } from 'ws';
import { createServer, IncomingMessage } from 'http';
import { URL } from 'url';

interface SessionMetadata {
  operatorId: string;
  alias: string | null;
  channel: string;
}

export class CommunicationHub {
  private activeSessions = new Map<WebSocket, SessionMetadata>();
  private hub: WebSocketServer;

  constructor(port: number) {
    const httpServer = createServer();
    this.hub = new WebSocketServer({ server: httpServer, path: '/stream' });
    this.configureListeners();
    httpServer.listen(port, () => c

πŸŽ‰ Mid-Year Sale β€” Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register β€” Start Free Trial

7-day free trial Β· Cancel anytime Β· 30-day money-back