Real-Time Communication

Modern applications frequently require data to update instantly without any manual user intervention. Traditional HTTP architecture relies on the client asking the server if new data exists. Real-time engineering reverses this paradigm, allowing the server to push updates to the client the exact moment an event occurs. Transitioning to this event-driven model requires fundamentally different infrastructure, as it introduces persistent, stateful connections to an otherwise stateless web environment.

Evaluating polling strategies

Before adopting complex bidirectional protocols, developers often attempt to simulate real-time behavior using standard HTTP requests. This is a common starting point but scales poorly.

  • Short polling: The client asks the server for new data at a fixed interval, such as every five seconds. This creates massive network overhead because the vast majority of requests return empty, wasting database and server resources.
  • Long polling: The client makes a request, and the server holds the connection open until new data is available to send. Once the data is delivered, the client immediately opens a new connection. This reduces empty responses but still carries the heavy payload of HTTP headers on every single cycle.

Learning Point: Polling is perfectly acceptable for low-frequency updates, such as a weather dashboard that refreshes every ten minutes. It is a severe architectural anti-pattern for chat applications, collaborative documents, or live financial tickers.

WebSockets: The bidirectional standard

For true bidirectional communication, the industry standard is the WebSocket protocol. It provides a full-duplex communication channel over a single TCP connection.

  • The connection handshake: A WebSocket begins as a standard HTTP request that is then formally upgraded to a persistent connection.
  • Bidirectional data flow: Both the client and the server can send data payloads to each other at any time without the traditional overhead of HTTP headers.
  • Stateful memory: Unlike REST APIs, the server must keep track of every active WebSocket connection in its memory to know exactly where to route outgoing messages.
The bidirectional standard

Server-Sent Events: The unidirectional alternative

Many applications only need the server to push data to the client, such as a live sports score or a progress bar for a long-running background job. In these cases, WebSockets introduce unnecessary complexity.

  • Unidirectional flow: Server-Sent Events (SSE) allow the server to push an endless stream of text data to the client over a standard, long-lived HTTP connection.
  • Native browser resilience: SSE uses the built-in EventSource interface in modern browsers. This interface automatically handles reconnections if the network drops, saving developers from writing complex client-side recovery logic.

Note: If your client never needs to send high-frequency data back to the server, SSE is architecturally simpler, easier to secure, and much easier to scale than WebSockets.

Connection resilience and heartbeats

Networks are inherently unreliable. A user might drive through a tunnel, causing their mobile device to silently drop the TCP connection without notifying the server.

  • Ping and pong frames: The server must periodically send a tiny “ping” message to the client. If the client does not respond with a “pong” within a specific timeframe, the server assumes the connection is dead and clears the memory.
  • Automatic reconnection: The client logic must be engineered to detect a dropped connection. It should attempt to reconnect using an exponential backoff strategy, which prevents a flood of simultaneous requests when a server restarts.
				
					/**
 * A simplified real-time client implementation.
 * It establishes a WebSocket connection and includes 
 * essential logic for handling unexpected disconnections.
 */
class RealTimeClient {
  constructor(endpointUrl) {
    this.url = endpointUrl;
    this.connect();
  }

  connect() {
    this.socket = new WebSocket(this.url);

    this.socket.onopen = () => {
      console.log("Persistent connection established.");
    };

    // Listen for data pushed directly from the backend
    this.socket.onmessage = (event) => {
      const payload = JSON.parse(event.data);
      this.handleServerEvent(payload);
    };

    // Trigger an automatic reconnection if the network drops
    this.socket.onclose = () => {
      console.log("Connection lost. Reconnecting in 5 seconds...");
      setTimeout(() => this.connect(), 5000);
    };
  }

  handleServerEvent(payload) {
    // Delegate the real-time data to the UI state manager
    if (payload.type === 'NEW_NOTIFICATION') {
      updateNotificationBadge(payload.data);
    }
  }
}

				
			

Scaling real-time infrastructure

Scaling stateless HTTP servers is as easy as adding more machines behind a load balancer. Scaling stateful WebSocket servers introduces severe architectural challenges that require a centralized broker.

  • The routing problem: If User A is connected to Server 1, and User B is connected to Server 2, a message sent from User A will not reach User B unless the servers have a way to communicate with each other.
  • The Pub/Sub pattern: Developers solve this by introducing a Message Broker like Redis. When Server 1 receives a message, it publishes that message to a Redis channel. Server 2 subscribes to that channel, receives the message instantly, and pushes it down to User B.
  • Sticky sessions: Load balancers must be configured to route a specific user to the exact same server for the duration of their session. This ensures the persistent connection is not accidentally broken by standard round-robin routing algorithms.
Scaling real-time infrastructure

Security in a persistent environment

Securing a continuous stream of data requires different strategies than securing individual REST endpoints.

  • Authentication timing: WebSockets cannot use standard HTTP authorization headers easily. You must pass a secure token during the initial connection handshake or immediately after the socket opens to verify the user.
  • Encrypted transport: You must always use the secure protocol variant to ensure the continuous data stream is encrypted via TLS. This prevents malicious actors from intercepting the live data feed over public networks.

By understanding the technical trade-offs between polling, WebSockets, and Server-Sent Events, you can build a responsive architecture that delivers live data efficiently without exhausting your server infrastructure.