WebSockets Chat App with Node.js and Socket.io
Traditional HTTP web architectures follow a strict request-response request lifecycle: the client sends an HTTP request, and the server returns a response before closing the connection. While this paradigm works well for standard REST APIs, it falls short for real-time applications such as live chat, collaborative editing boards, and real-time telemetry dashboards where servers need to push data instantly to connected clients.
The HTML5 **WebSocket** protocol solves this by establishing a persistent, full-duplex communication channel over a single TCP connection. In the Node.js ecosystem, **Socket.io** is the premier library that abstracts WebSocket management, automatic reconnection logic, and fallback transports. This comprehensive tutorial walks through building a fully functional real-time chat application from scratch using Node.js, Express, and Socket.io.
1. Project Initialization and Dependencies
Create a new project directory, initialize Node.js, and install `express` and `socket.io`.
# Terminal setup commands
mkdir websocket-chat-app
cd websocket-chat-app
npm init -y
npm install express socket.io
npm install --save-dev nodemon
2. Configuring the Node.js and Socket.io Server
Create a `server.js` file. Note that unlike standard Express apps where you listen on `app.listen()`, Socket.io requires attaching to the raw Node.js `http` server instance so both HTTP requests and WebSocket upgrade handshakes share the same port.
// server.js
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server);
// Serve static frontend files from 'public' directory
app.use(express.static('public'));
// Handle Socket.io connection events
io.on('connection', (socket) => {
console.log(`User connected: ${socket.id}`);
// Broadcast incoming chat message to all connected clients
socket.on('chat_message', (data) => {
io.emit('chat_message', {
username: data.username,
message: data.message,
timestamp: new Date().toLocaleTimeString()
});
});
// Handle client disconnection
socket.on('disconnect', () => {
console.log(`User disconnected: ${socket.id}`);
});
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`🚀 Chat server running on http://localhost:${PORT}`);
});
3. Building the Frontend Chat UI and Client Script
Create a `public` directory and add an `index.html` file containing the chat interface and Socket.io client script.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Real-Time Chat App</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css">
</head>
<body class="bg-gray-100 h-screen flex flex-col justify-between">
<div class="bg-blue-600 text-white p-4 text-center font-bold text-xl">
Node.js & Socket.io Live Chat
</div>
<div id="chat-window" class="flex-grow p-4 overflow-y-auto max-w-2xl mx-auto w-full space-y-3">
<!-- Messages injected dynamically -->
</div>
<form id="chat-form" class="bg-white p-4 max-w-2xl mx-auto w-full flex shadow-lg">
<input type="text" id="username" placeholder="Your Name..." class="border p-2 rounded mr-2 w-1/4" required>
<input type="text" id="message" placeholder="Type a message..." class="border p-2 rounded flex-grow mr-2" required>
<button type="submit" class="bg-blue-600 text-white px-6 rounded">Send</button>
</form>
<!-- Socket.io Client SDK -->
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
const form = document.getElementById('chat-form');
const inputMsg = document.getElementById('message');
const inputUser = document.getElementById('username');
const chatWindow = document.getElementById('chat-window');
form.addEventListener('submit', (e) => {
e.preventDefault();
if (inputMsg.value && inputUser.value) {
socket.emit('chat_message', {
username: inputUser.value,
message: inputMsg.value
});
inputMsg.value = '';
}
});
socket.on('chat_message', (data) => {
const div = document.createElement('div');
div.className = "p-2 bg-white rounded shadow-sm";
div.innerHTML = `<span class="font-bold text-blue-600">${data.username}</span> <span class="text-xs text-gray-400">[${data.timestamp}]</span>: <p>${data.message}</p>`;
chatWindow.appendChild(div);
chatWindow.scrollTop = chatWindow.scrollHeight;
});
</script>
</body>
</html>
Conclusion
By combining Node.js, Express, and Socket.io, developers can build robust, bi-directional real-time chat applications with minimal code complexity and high scalability.