Node.js cookbook · Advanced

Scale real-time Socket.IO across instances

Broadcast chat messages to rooms when users are connected to different Node instances.

Approach

Use Socket.IO with the Redis adapter so io.to(room).emit() reaches sockets on every instance. Authenticate in a connection middleware, authorize room joins, validate and rate-limit incoming events, and enable sticky sessions at the load balancer if HTTP long-polling fallback is used.

Solution

import { createServer } from "node:http";
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";

const httpServer = createServer(app);
const pub = createClient({ url: process.env.REDIS_URL });
const sub = pub.duplicate();
await Promise.all([pub.connect(), sub.connect()]);

const io = new Server(httpServer, { adapter: createAdapter(pub, sub), cors: { origin: "https://app.example.com", credentials: true } });

io.use(async (socket, next) => {
  try {
    socket.data.user = await verifyToken(socket.handshake.auth.token);
    next();
  } catch {
    next(new Error("unauthorized"));
  }
});

io.on("connection", (socket) => {
  socket.on("join", async (roomId: string, ack) => {
    if (!(await rooms.canJoin(socket.data.user.id, roomId))) return ack?.({ ok: false });
    await socket.join(`room:${roomId}`);
    ack?.({ ok: true });
  });
  socket.on("message", async ({ roomId, text }: { roomId: string; text: string }) => {
    if (!socket.rooms.has(`room:${roomId}`) || typeof text !== "string" || text.length > 2000) return;
    const message = await messages.save(roomId, socket.data.user.id, text);
    io.to(`room:${roomId}`).emit("message", message);
  });
});

httpServer.listen(3000);

Pitfalls

  • No auth on the socket handshake.
  • Trusting client-sent room IDs without authorization.
  • Missing sticky sessions with polling transport across instances.

Variations

  • Plain ws + Redis pub/sub for minimal overhead.
  • Managed realtime services for global scale.

Further reading: Socket.IO: Redis adapter

Reference: https://socket.io/docs/v4/redis-adapter/

More Node.js recipes