Node.js WebSocket — Complete Guide to ws Library and Socket.IO
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
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