WebSockets with Node.js — Real-Time Apps
Advertisement
WebSockets enable persistent connections for real-time, bidirectional communication. Perfect for chat, notifications, and live data.
HTTP vs WebSockets
HTTP: Request-response, stateless, overhead WebSockets: Persistent connection, full-duplex, low overhead
Using ws Library
npm install ws
npm install --save-dev @types/ws
import WebSocket from "ws";
import http from "http";
const server = http.createServer();
const wss = new WebSocket.Server({ server });
wss.on("connection", (ws) => {
console.log("Client connected");
ws.on("message", (data) => {
console.log("Received:", data);
// Broadcast to all clients
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
ws.on("close", () => {
console.log("Client disconnected");
});
ws.on("error", (error) => {
console.error("WebSocket error:", error);
});
// Send message to client
ws.send(JSON.stringify({ type: "welcome", message: "Connected" }));
});
server.listen(8080, () => {
console.log("WebSocket server running on port 8080");
});
Socket.io
Socket.io provides easier API and fallbacks:
npm install socket.io socket.io-client
import { createServer } from "http";
import { Server } from "socket.io";
const httpServer = createServer();
const io = new Server(httpServer, {
cors: { origin: "*" },
});
io.on("connection", (socket) => {
console.log(`User ${socket.id} connected`);
// Listen for events
socket.on("chat", (message) => {
console.log("Message:", message);
// Broadcast to all
io.emit("chat", {
userId: socket.id,
message,
timestamp: new Date(),
});
});
// Send event to specific user
socket.emit("welcome", { message: "Welcome to chat" });
// Join room
socket.on("join-room", (roomId) => {
socket.join(roomId);
});
// Send to room
socket.on("room-message", (roomId, message) => {
io.to(roomId).emit("message", message);
});
socket.on("disconnect", () => {
console.log(`User ${socket.id} disconnected`);
});
});
httpServer.listen(3000, () => {
console.log("Socket.io server running on port 3000");
});
- HTTP vs WebSockets
- Using ws Library
- Socket.io
- Client-Side Usage
- Chat Application Example
- Namespaces and Rooms
- Authentication
- Comparison: WebSockets vs Server-Sent Events vs Polling
- FAQ
Client-Side Usage
import io from "socket.io-client";
const socket = io("http://localhost:3000");
socket.on("connect", () => {
console.log("Connected to server");
});
socket.on("welcome", (data) => {
console.log(data);
});
socket.on("chat", (data) => {
console.log(`${data.userId}: ${data.message}`);
});
// Send event to server
socket.emit("chat", "Hello, World!");
// Join a room
socket.emit("join-room", "room-123");
// Send to room
socket.emit("room-message", "room-123", "Hello, room!");
socket.on("disconnect", () => {
console.log("Disconnected from server");
});
Chat Application Example
// Server
const io = new Server(httpServer);
const users = new Map<string, string>();
io.on("connection", (socket) => {
socket.on("login", (username) => {
users.set(socket.id, username);
io.emit("users", Array.from(users.values()));
});
socket.on("message", (message) => {
const username = users.get(socket.id);
io.emit("message", {
username,
text: message,
timestamp: new Date(),
});
});
socket.on("disconnect", () => {
users.delete(socket.id);
io.emit("users", Array.from(users.values()));
});
});
// Client
let username: string;
const usernameInput = document.getElementById("username") as HTMLInputElement;
const sendBtn = document.getElementById("send") as HTMLButtonElement;
const messagesDiv = document.getElementById("messages") as HTMLDivElement;
const usersDiv = document.getElementById("users") as HTMLDivElement;
sendBtn.addEventListener("click", () => {
const message = (document.getElementById("input") as HTMLInputElement).value;
socket.emit("message", message);
});
socket.on("message", (data) => {
const el = document.createElement("div");
el.textContent = `${data.username}: ${data.text}`;
messagesDiv.appendChild(el);
});
socket.on("users", (userList) => {
usersDiv.innerHTML = userList.map((u) => `<p>${u}</p>`).join("");
});
usernameInput.addEventListener("change", (e) => {
username = (e.target as HTMLInputElement).value;
socket.emit("login", username);
});
Namespaces and Rooms
// Namespaces - separate communication channels
const chatNs = io.of("/chat");
const notificationNs = io.of("/notifications");
chatNs.on("connection", (socket) => {
socket.on("message", (msg) => {
chatNs.emit("message", msg);
});
});
notificationNs.on("connection", (socket) => {
socket.on("subscribe", (topic) => {
socket.join(topic);
});
socket.on("notify", (topic, notification) => {
notificationNs.to(topic).emit("notification", notification);
});
});
// Client
const chat = io.connect("http://localhost:3000/chat");
const notifications = io.connect("http://localhost:3000/notifications");
Authentication
const io = new Server(httpServer, {
connectionStateRecovery: {
maxDisconnectionDuration: 2 * 60 * 1000,
},
});
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) {
return next(new Error("Authentication failed"));
}
try {
const decoded = jwt.verify(token, "secret");
socket.userId = (decoded as any).userId;
next();
} catch {
next(new Error("Invalid token"));
}
});
io.on("connection", (socket) => {
console.log(`User ${socket.userId} connected`);
});
// Client
const socket = io("http://localhost:3000", {
auth: {
token: localStorage.getItem("token"),
},
});
Comparison: WebSockets vs Server-Sent Events vs Polling
| Feature | WebSockets | SSE | Polling |
|---|---|---|---|
| Direction | Bidirectional | Server→Client | Client→Server |
| Latency | Low (~5ms) | Low (~100ms) | High (seconds) |
| Overhead | Low | Low | High |
| Browser support | All modern | All modern | All |
| Complexity | Medium | Low | Very low |
FAQ
Q: Should I use WebSockets for everything real-time? A: No. SSE is simpler for server→client only. Use WebSockets for two-way communication.
Q: How many concurrent connections can Node.js handle? A: Thousands easily. Architecture matters more than technology.
Q: How do I scale WebSockets across multiple servers? A: Use a message broker (Redis Pub/Sub) to coordinate between servers.
WebSockets bring real-time capabilities to web applications. Combined with Socket.io, they make building collaborative and live-updating applications straightforward.
Advertisement