Node.js WebSockets Skill
Master real-time bidirectional communication for building chat apps, live notifications, collaborative tools, and real-time dashboards.
Quick Start
WebSocket server in 3 steps:
Setup Server - Socket.io or ws library Handle Connections - Manage client lifecycle Emit Events - Send/receive messages Core Concepts Socket.io Server Setup const express = require('express'); const { createServer } = require('http'); const { Server } = require('socket.io');
const app = express(); const httpServer = createServer(app); const io = new Server(httpServer, { cors: { origin: 'http://localhost:3000', methods: ['GET', 'POST'] } });
io.on('connection', (socket) => { console.log('User connected:', socket.id);
// Handle events socket.on('chat:message', (data) => { // Broadcast to all clients io.emit('chat:message', { ...data, timestamp: Date.now() }); });
// Join rooms socket.on('room:join', (roomId) => { socket.join(roomId); socket.to(roomId).emit('room:user-joined', socket.id); });
// Handle disconnect socket.on('disconnect', () => { console.log('User disconnected:', socket.id); }); });
httpServer.listen(3000);
Socket.io Client import { io } from 'socket.io-client';
const socket = io('http://localhost:3000', { auth: { token: 'jwt-token-here' } });
socket.on('connect', () => { console.log('Connected:', socket.id); });
socket.on('chat:message', (message) => { console.log('Received:', message); });
// Send message socket.emit('chat:message', { text: 'Hello!', userId: 'user123' });
Learning Path Beginner (1-2 weeks) ✅ Socket.io server setup ✅ Basic event emission ✅ Connect/disconnect handling ✅ Simple chat application Intermediate (3-4 weeks) ✅ Rooms and namespaces ✅ Authentication middleware ✅ Broadcasting patterns ✅ Error handling Advanced (5-6 weeks) ✅ Horizontal scaling with Redis ✅ Binary data transfer ✅ Reconnection strategies ✅ Performance optimization Native ws Library const WebSocket = require('ws'); const { createServer } = require('http');
const server = createServer(); const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => { const ip = req.socket.remoteAddress; console.log('Client connected from', ip);
// Handle messages ws.on('message', (data) => { const message = JSON.parse(data); console.log('Received:', message);
// Echo back
ws.send(JSON.stringify({
type: 'echo',
data: message
}));
});
// Ping/pong heartbeat ws.isAlive = true; ws.on('pong', () => { ws.isAlive = true; });
ws.on('close', () => { console.log('Client disconnected'); }); });
// Heartbeat interval const interval = setInterval(() => { wss.clients.forEach((ws) => { if (!ws.isAlive) return ws.terminate(); ws.isAlive = false; ws.ping(); }); }, 30000);
wss.on('close', () => clearInterval(interval));
server.listen(3000);
Rooms and Namespaces // Namespaces for feature separation const chatNamespace = io.of('/chat'); const notificationsNamespace = io.of('/notifications');
chatNamespace.on('connection', (socket) => { // Chat-specific logic socket.on('message', (msg) => { chatNamespace.emit('message', msg); }); });
notificationsNamespace.on('connection', (socket) => { // Notification-specific logic socket.on('subscribe', (topic) => { socket.join(topic); }); });
// Rooms within namespace
socket.on('join-channel', (channelId) => {
socket.join(channel:${channelId});
// Send only to room
io.to(channel:${channelId}).emit('user-joined', {
userId: socket.userId,
channelId
});
});
// Leave room
socket.on('leave-channel', (channelId) => {
socket.leave(channel:${channelId});
});
Authentication Middleware const jwt = require('jsonwebtoken');
// Socket.io middleware io.use((socket, next) => { const token = socket.handshake.auth.token;
if (!token) { return next(new Error('Authentication required')); }
try { const decoded = jwt.verify(token, process.env.JWT_SECRET); socket.userId = decoded.id; socket.userRole = decoded.role; next(); } catch (err) { next(new Error('Invalid token')); } });
// Namespace-level auth const adminNamespace = io.of('/admin'); adminNamespace.use((socket, next) => { if (socket.userRole !== 'admin') { return next(new Error('Admin access required')); } next(); });
Scaling with Redis Adapter const { createAdapter } = require('@socket.io/redis-adapter'); const { createClient } = require('redis');
const pubClient = createClient({ url: 'redis://localhost:6379' }); const subClient = pubClient.duplicate();
Promise.all([pubClient.connect(), subClient.connect()]).then(() => { io.adapter(createAdapter(pubClient, subClient)); console.log('Redis adapter connected'); });
// Now events are broadcast across all server instances io.emit('notification', { message: 'Hello from any server!' });
Real-Time Patterns Chat Application // Server io.on('connection', (socket) => { socket.on('chat:join', ({ room, username }) => { socket.join(room); socket.to(room).emit('chat:user-joined', { username }); });
socket.on('chat:message', ({ room, message }) => { io.to(room).emit('chat:message', { user: socket.userId, message, timestamp: Date.now() }); });
socket.on('chat:typing', ({ room }) => { socket.to(room).emit('chat:typing', { user: socket.userId }); }); });
Live Notifications
// Server
function sendNotification(userId, notification) {
io.to(user:${userId}).emit('notification', notification);
}
// Join user's personal room
socket.on('authenticate', () => {
socket.join(user:${socket.userId});
});
// From any service notificationService.on('new', (userId, data) => { sendNotification(userId, data); });
Live Dashboard // Server: broadcast metrics periodically setInterval(async () => { const metrics = await getSystemMetrics(); io.emit('metrics:update', metrics); }, 1000);
// Or on-demand updates
database.onChange((change) => {
io.to(dashboard:${change.collection}).emit('data:update', change);
});
Error Handling // Client-side reconnection const socket = io({ reconnection: true, reconnectionAttempts: 5, reconnectionDelay: 1000, reconnectionDelayMax: 5000 });
socket.on('connect_error', (err) => { console.log('Connection error:', err.message); });
socket.on('reconnect', (attemptNumber) => { console.log('Reconnected after', attemptNumber, 'attempts'); });
socket.on('reconnect_failed', () => { console.log('Failed to reconnect'); // Fallback to polling or show offline UI });
// Server-side error handling socket.on('error', (error) => { console.error('Socket error:', error); });
Unit Test Template const { createServer } = require('http'); const { Server } = require('socket.io'); const Client = require('socket.io-client');
describe('WebSocket Server', () => { let io, serverSocket, clientSocket;
beforeAll((done) => {
const httpServer = createServer();
io = new Server(httpServer);
httpServer.listen(() => {
const port = httpServer.address().port;
clientSocket = Client(http://localhost:${port});
io.on('connection', (socket) => {
serverSocket = socket;
});
clientSocket.on('connect', done);
});
});
afterAll(() => { io.close(); clientSocket.close(); });
it('should receive message from client', (done) => { serverSocket.on('hello', (arg) => { expect(arg).toBe('world'); done(); }); clientSocket.emit('hello', 'world'); });
it('should broadcast to clients', (done) => { clientSocket.on('broadcast', (arg) => { expect(arg).toBe('everyone'); done(); }); io.emit('broadcast', 'everyone'); }); });
Troubleshooting Problem Cause Solution Connection drops No heartbeat Enable pingInterval/pingTimeout Messages not received Wrong room Verify room membership Scaling issues No Redis Add Redis adapter Memory leak Listeners not removed Clean up on disconnect When to Use
Use WebSockets when:
Real-time bidirectional communication Low-latency updates needed Live notifications/chat Collaborative applications Gaming/trading platforms Related Skills Express REST API (HTTP fallback) Microservices (distributed events) Redis (pub/sub scaling) Resources Socket.io Documentation ws Library Redis Adapter