Skip to content

Node.js WebSocket — Complete Guide to ws Library and Socket.IO

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Node.js Websocket. We cover key concepts, practical examples, and best practices to help you master this topic.

Node.js WebSocket enables full-duplex real-time communication between server and clients, supporting low-latency messaging with libraries like ws for raw sockets and Socket.IO for reliability features.

What You'll Learn

By the end of this tutorial, you'll build WebSocket servers with ws and Socket.IO, handle connections and disconnections, implement rooms, authenticate connections, and scale across multiple nodes.

Why WebSocket Matters

HTTP is request-response. WebSocket provides persistent bidirectional channels needed for live chat, gaming, collaborative editing, financial tickers, and real-time dashboards.

Real-World Use

A collaborative document editor uses WebSocket to broadcast edits to all connected clients in real time. Socket.IO provides automatic reconnection when the network drops temporarily.

WebSocket Path

flowchart LR
  A[GraphQL] --> B[WebSocket]
  B --> C[Real-Time Apps]
  C --> D[Scaling]
  D --> E[Security]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Raw WebSocket with ws

The ws library provides a lightweight WebSocket server with minimal overhead.

const { WebSocketServer } = require("ws");
const http = require("node:http");
const server = http.createServer((req, res) => res.end("WebSocket server running"));
const wss = new WebSocketServer({ server, path: "/ws" });
wss.on("connection", (ws, req) => {
  console.log("Client connected from", req.socket.remoteAddress);
  ws.on("message", (data) => {
    console.log("Received:", data.toString());
    ws.send(`Echo: ${data}`);
  });
  ws.on("close", () => console.log("Client disconnected"));
  ws.send("Welcome to WebSocket server");
});
server.listen(3000);

Socket.IO Setup

Socket.IO provides automatic reconnection, rooms, namespaces, and fallback transports.

const express = require("express");
const { createServer } = require("node:http");
const { Server } = require("socket.io");
const app = express();
const server = createServer(app);
const io = new Server(server, {
  cors: { origin: "https://app.example.com", methods: ["GET", "POST"] },
  pingInterval: 10000,
  pingTimeout: 5000,
  transports: ["websocket", "polling"],
});
io.on("connection", (socket) => {
  console.log("Socket connected:", socket.id);
  socket.on("disconnect", (reason) => console.log("Disconnected:", reason));
});
server.listen(3000);

Room Management

Socket.IO rooms group clients for targeted message broadcasting.

const io = new Server(server);
io.on("connection", (socket) => {
  socket.on("join-room", (roomId) => {
    socket.join(roomId);
    io.to(roomId).emit("notification", `User ${socket.id} joined room ${roomId}`);
  });
  socket.on("leave-room", (roomId) => {
    socket.leave(roomId);
    io.to(roomId).emit("notification", `User ${socket.id} left room ${roomId}`);
  });
  socket.on("room-message", ({ roomId, message }) => {
    io.to(roomId).emit("message", { from: socket.id, message, timestamp: Date.now() });
  });
});

Authentication

Authenticate WebSocket connections using tokens during the handshake.

const jwt = require("jsonwebtoken");
const { Server } = require("socket.io");
const io = new Server(server);
io.use((socket, next) => {
  const token = socket.handshake.auth.token;
  if (!token) return next(new Error("Authentication required"));
  try {
    const user = jwt.verify(token, process.env.JWT_SECRET);
    socket.user = user;
    next();
  } catch {
    next(new Error("Invalid token"));
  }
});
io.on("connection", (socket) => {
  console.log("Authenticated user:", socket.user.id);
  socket.join(`user:${socket.user.id}`);
});

Scaling with Redis Adapter

Scale Socket.IO across multiple Node.js instances using Redis for message broadcasting.

const { createClient } = require("redis");
const { Server } = require("socket.io");
const { createAdapter } = require("@socket.io/redis-adapter");
const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();
const io = new Server(server);
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
  io.adapter(createAdapter(pubClient, subClient));
  io.on("connection", (socket) => {
    console.log("Connected to scaled instance");
  });
});

Common Mistakes

1. Not Handling Disconnection Cleanup

Active listeners, intervals, or subscriptions must be cleaned up on disconnect to prevent memory leaks.

2. Broadcasting Without Rate Limiting

Unlimited message broadcasting can overwhelm the server. Implement message rate limiting per socket.

3. Storing Socket References in Memory

Sockets in a Map across restarts lose connections. Use Redis or database for presistent state.

4. Ignoring Transport Fallbacks

Some networks block WebSocket. Socket.IO automatically falls back to HTTP long-polling.

5. Not Validating Message Payloads

Malformed messages crash parsers. Validate all incoming message data with schemas.

Practice Questions

1. What is the difference between WebSocket and HTTP?

WebSocket maintains a persistent bidirectional connection. HTTP is request-response, closing after each response.

2. How does Socket.IO handle reconnection?

It automatically attempts to reconnect with exponential backoff when the connection drops.

3. What are Socket.IO rooms?

Logical groups of sockets. Messages sent to a room reach all sockets in that group.

4. How do you scale Socket.IO across multiple servers?

Use a Redis adapter to broadcast messages across all instances via pub/sub.

5. Challenge: Create a WebSocket server with authenticated rooms for a chat application.

const { Server } = require("socket.io");
const jwt = require("jsonwebtoken");
const io = new Server(server);
io.use((s, n) => { try { s.user = jwt.verify(s.handshake.auth.token, "secret"); n(); } catch { n(new Error("Unauthorized")); }});
io.on("connection", (s) => {
  s.on("join", (room) => { s.join(room); io.to(room).emit("joined", s.user.id); });
  s.on("chat", ({ room, msg }) => io.to(room).emit("chat", { user: s.user.id, msg }));
});

FAQ

What is the difference between ws and Socket.IO?

ws is a minimal WebSocket library. Socket.IO adds rooms, namespaces, reconnection, and fallback transports.

Does WebSocket work through load balancers?

Yes, with sticky sessions or a shared adapter like Redis for Socket.IO.

How do I broadcast to all connected clients?

io.emit() in Socket.IO sends to all. wss.clients.forEach(c => c.send()) in ws.

What is the maximum message size in WebSocket?

Default is 1MB in ws and Socket.IO. Configure with maxPayload or maxHttpBufferSize.

Is WebSocket secure?

Use wss:// (TLS) and authenticate connections. WebSocket is as secure as the underlying TLS.

Mini Project: Real-Time Chat Server

Build a complete real-time chat server with rooms and authentication.

const { Server } = require("socket.io");
const { createServer } = require("node:http");
const server = createServer();
const io = new Server(server, { cors: { origin: "*" } });
const rooms = new Map();
io.use((socket, next) => {
  const token = socket.handshake.auth.token;
  if (token === "valid-token") return next();
  next(new Error("Invalid token"));
});
io.on("connection", (socket) => {
  socket.on("create-room", (name) => {
    rooms.set(name, new Set());
    socket.join(name);
    rooms.get(name).add(socket.id);
  });
  socket.on("send-message", ({ room, text }) => {
    io.to(room).emit("message", { from: socket.id, text, time: new Date().toISOString() });
  });
});
server.listen(3001);

What's Next

Node.js Real-Time Applications Node.js GraphQL Subscriptions Node.js Event Emitter

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro