Building Realtime Apps with WebSockets
Create interactive, real-time experiences using WebSockets and Node.js.
Introduction
The traditional HTTP request-response model is great for fetching resources, but it falls short when you need real-time communication. Whether it's a chat application, a live dashboard, or a multiplayer game, WebSockets provide a persistent, bidirectional communication channel between the client and the server.
In this tutorial, we'll build a simple real-time chat application using Node.js and socket.io.
What are WebSockets?
WebSockets allow for full-duplex communication over a single TCP connection. Unlike HTTP polling, where the client repeatedly asks the server "do you have new data?", WebSockets allow the server to push data to the client instantly.
Setting Up the Server
We'll use express for the HTTP server and socket.io for WebSocket handling.
npm init -y
npm install express socket.io
Create 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);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('chat message', (msg) => {
io.emit('chat message', msg); // Broadcast to all clients
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
server.listen(3000, () => {
console.log('listening on *:3000');
});
Creating the Client
Create index.html:
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }
#form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
#input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
#input:focus { outline: none; }
#form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages > li { padding: 0.5rem 1rem; }
#messages > li:nth-child(odd) { background: #efefef; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form id="form" action="">
<input id="input" autocomplete="off" /><button>Send</button>
</form>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
var form = document.getElementById('form');
var input = document.getElementById('input');
form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('chat message', input.value);
input.value = '';
}
});
socket.on('chat message', function(msg) {
var item = document.createElement('li');
item.textContent = msg;
document.getElementById('messages').appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
</script>
</body>
</html>
Scaling with Redis
When you scale to multiple server instances, a user connected to Server A won't receive messages sent by a user on Server B. To solve this, we use a Redis Adapter.
The Redis Adapter publishes events to a Redis channel, which other server instances subscribe to, ensuring messages are propagated across the entire cluster.
Conclusion
WebSockets are a powerful tool for modern web development. Libraries like Socket.io make it easy to implement robust real-time features with fallbacks for older browsers and built-in support for rooms and namespaces.