Running Code Locally
WebSocket examples require a server to connect to. For the best experience: Download Node.js to run a local WebSocket server, use your browser's Developer Console (Press F12) to test the WebSocket API, or try online WebSocket testing services.
Working With WebSockets for Real-Time Features
Master real-time communication: persistent connections, messaging patterns, presence, rooms, and scalable architecture.
What You'll Learn
- WebSocket connection lifecycle
- Auto-reconnection with backoff
- Heartbeat & presence systems
- Room/channel management
- Reliable delivery with ACKs
- Scaling with Pub/Sub
What WebSockets Actually Are
HTTP is request → response only. WebSockets allow persistent, two-way communication: the server can push events anytime, and the client can send events anytime — no need for repeated HTTP requests.
HTTP (Traditional)
- Request → Response → Connection closed
- Client must poll for updates
- High latency, wasted bandwidth
- Server can't push data
WebSockets
- Persistent connection stays open
- Server pushes updates instantly
- Low latency, efficient
- Perfect for real-time apps
WebSockets Power:
Chat systems • Live notifications • Real-time games • Stock tickers • Collaborative editing • Multiplayer interactions • Typing indicators • Presence systems • Live dashboards
Basic WebSocket Connection
The browser's WebSocket API is simple but powerful. Always use wss:// for secure connections.
Basic WebSocket Connection
Learn the browser WebSocket API fundamentals
// Basic WebSocket Connection
const socket = new WebSocket("wss://echo.websocket.org");
// Connection opened
socket.addEventListener("open", (event) => {
console.log("✅ Connected to WebSocket server!");
// Send a message
socket.send("Hello Server!");
});
// Listen for messages
socket.addEventListener("message", (event) => {
console.log("📨 Message from server:", event.data);
});
// Connection closed
socket.addEventListener("close", (event) => {
console.log("❌ Disconnected from se
...Real-Time Messaging with JSON
WebSockets only send text or binary. Best practice is to use structured JSON messages with type fields to handle different event types.
Real-Time Messaging with JSON
Structure messages with type fields for different events
// Real-Time Messaging with JSON
// Message types for a chat app
const MessageTypes = {
CHAT: 'chat',
TYPING: 'typing',
PRESENCE: 'presence',
JOIN_ROOM: 'join_room',
LEAVE_ROOM: 'leave_room',
ACK: 'ack',
ERROR: 'error'
};
// Create a structured message
function createMessage(type, payload) {
return JSON.stringify({
id: crypto.randomUUID(),
type,
payload,
timestamp: Date.now()
});
}
// Send chat message
const chatMessage = createMessage(MessageTypes.CHAT, {
...Robust Reconnection Logic
WebSocket connections will always drop eventually (network issues, server restarts, etc.). Production apps must auto-recover with exponential backoff.
Robust Reconnection Logic
Auto-recover with exponential backoff
// Robust Reconnection Logic with Exponential Backoff
class ReconnectingWebSocket {
constructor(url, options = {}) {
this.url = url;
this.maxRetries = options.maxRetries || 10;
this.baseDelay = options.baseDelay || 1000;
this.maxDelay = options.maxDelay || 30000;
this.retries = 0;
this.socket = null;
this.listeners = new Map();
this.messageQueue = [];
this.connect();
}
connect() {
console.log(`Connecting to ${this.url}...`);
th
...Heartbeat (Ping/Pong) System
Every WebSocket system needs a heartbeat to detect dead connections. Clients ping periodically; if no pong returns, the connection is considered dead.
Heartbeat (Ping/Pong) System
Detect dead connections with periodic pings
// Heartbeat (Ping/Pong) System for Dead Connection Detection
class HeartbeatWebSocket {
constructor(url, options = {}) {
this.url = url;
this.pingInterval = options.pingInterval || 30000;
this.pongTimeout = options.pongTimeout || 5000;
this.socket = null;
this.pingTimer = null;
this.pongTimer = null;
this.isAlive = false;
this.connect();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
consol
...Presence System (Online/Offline Tracking)
Presence tracking shows who's online. It requires careful handling because disconnects aren't always clean (network loss, device sleep, etc.).
Presence System
Track online/offline status of users
// Presence System (Online/Offline Tracking)
class PresenceManager {
constructor() {
this.users = new Map();
this.listeners = [];
}
// Track user coming online
userJoined(userId, metadata = {}) {
const user = {
id: userId,
status: "online",
joinedAt: Date.now(),
lastSeen: Date.now(),
...metadata
};
this.users.set(userId, user);
this.notify("join", user);
console.log(`✅ ${userId} joined (total: ${this.users.size})`);
...Rooms & Channels
Real apps need grouping: chat rooms, game lobbies, per-user channels. Rooms allow targeted messaging instead of broadcasting to everyone.
Rooms & Channels
Group users for targeted messaging
// Room/Channel System for Targeted Messaging
class RoomManager {
constructor() {
this.rooms = new Map();
this.userRooms = new Map(); // userId -> Set of roomIds
}
// Create a room
createRoom(roomId, options = {}) {
if (this.rooms.has(roomId)) {
return this.rooms.get(roomId);
}
const room = {
id: roomId,
name: options.name || roomId,
members: new Set(),
createdAt: Date.now(),
isPrivate: options.isPrivate || false,
max
...Reliable Delivery with ACKs
WebSockets do NOT guarantee message delivery. For critical messages, implement ACKs (acknowledgments) and retry logic.
Reliable Delivery with ACKs
Implement acknowledgments and retry logic
// Reliable Delivery with ACKs and Retries
class ReliableMessaging {
constructor(socket) {
this.socket = socket;
this.pendingMessages = new Map();
this.retryDelay = 2000;
this.maxRetries = 3;
}
// Send with guaranteed delivery
sendReliable(message) {
const id = crypto.randomUUID();
const wrappedMessage = {
id,
...message,
timestamp: Date.now()
};
// Store for retry
this.pendingMessages.set(id, {
message: wrappedMessage,
...WebSocket Security
WebSockets must be validated like any untrusted API. Always authenticate, validate messages, sanitize input, and rate limit.
WebSocket Security
Validate, sanitize, and rate-limit messages
// WebSocket Security Best Practices
// 1. Validate all incoming messages
function validateMessage(data) {
// Parse safely
let message;
try {
message = JSON.parse(data);
} catch {
console.error("Invalid JSON");
return null;
}
// Check required fields
if (!message.type || typeof message.type !== "string") {
console.error("Missing or invalid type");
return null;
}
// Whitelist allowed message types
const allowedTypes = ["chat", "typing", "presence", "
...Scaling with Pub/Sub Pattern
A single server can't handle millions of users. Real systems use Redis/NATS/Kafka as a message bus to broadcast across multiple servers.
Scaling with Pub/Sub Pattern
Broadcast across multiple servers with Redis
// Scaling WebSockets with Pub/Sub Pattern
// Simulated Redis-like Pub/Sub for cross-server communication
class PubSub {
constructor() {
this.channels = new Map();
}
subscribe(channel, callback) {
if (!this.channels.has(channel)) {
this.channels.set(channel, new Set());
}
this.channels.get(channel).add(callback);
console.log("Subscribed to channel: " + channel);
}
unsubscribe(channel, callback) {
this.channels.get(channel)?.delete(callback);
}
...📡 WebSocket Mastery Summary
- WebSockets provide persistent, bidirectional communication
- Use wss:// for secure connections
- Structure messages with JSON and type fields
- Implement auto-reconnection with exponential backoff
- Use heartbeats to detect dead connections
- Build presence systems for online/offline tracking
- Organize users into rooms/channels
- Use ACKs and retries for reliable delivery
- Validate, sanitize, and rate-limit all messages
- Scale with Pub/Sub (Redis) across multiple servers
Sign up for free to track which lessons you've completed and get learning reminders.