Back to all posts

Building Real-time Applications

Web DevelopmentFebruary 28, 20249 min read
WebSocketReal-timeBackend

Real-time features are now expected in most modern apps. From chat apps to live dashboards, users want instant updates without refreshing the page. This post looks at the main technologies and patterns that make that possible.

WebSocket Fundamentals

WebSocket provides a persistent, full-duplex connection between client and server. Unlike HTTP's request-response model, WebSocket allows both parties to send data at any time, making it ideal for real-time communication.

typescript
// Server (Node.js)
import { WebSocketServer } from "ws";

const wss = new WebSocketServer({ port: 8080 });

wss.on("connection", (ws) => {
  ws.on("message", (data) => {
    // Broadcast to all connected clients
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(data.toString());
      }
    });
  });
});

Socket.IO for Production

While raw WebSocket works well, Socket.IO adds critical production features: automatic reconnection, room-based messaging, acknowledgments, and fallback transports for environments where WebSocket is blocked.

typescript
// Server
import { Server } from "socket.io";

const io = new Server(3001, { cors: { origin: "*" } });

io.on("connection", (socket) => {
  socket.on("join-room", (roomId: string) => {
    socket.join(roomId);
  });

  socket.on("message", ({ roomId, text }) => {
    io.to(roomId).emit("message", {
      text,
      sender: socket.id,
      timestamp: Date.now(),
    });
  });
});

Choosing the Right Technology

The choice between WebSocket, Socket.IO, Server-Sent Events (SSE), and Firebase depends on your use case and constraints.

  • WebSocket: Low-level, full control, best for custom protocols
  • Socket.IO: Production-ready with rooms, reconnection, and broadcasting
  • SSE: Server-to-client only, simpler than WebSocket, works over HTTP/2
  • Firebase Realtime DB: Fully managed, best for rapid prototyping and mobile apps

Scaling Real-time Systems

Scaling WebSocket connections is fundamentally different from scaling HTTP. Each connection maintains state, so you need strategies for distributing connections across servers while ensuring messages reach the right clients.

  • Use Redis pub/sub as a message broker between server instances
  • Implement sticky sessions at the load balancer level
  • Consider connection limits per server (typically 10k-50k per instance)
  • Use heartbeats to detect and clean up stale connections

Real-time features add complexity to your architecture, but the user experience gains are substantial. Start with the simplest solution that meets your requirements and scale up as needed.