Pub/Sub Architecture with Node.js and Redis: Scalable Chat Application
What will you learn in this guide?
In this guide, you will learn the Publish/Subscribe (Pub/Sub) architecture, one of the basic building blocks of real-time applications.
You will develop a chat application that works with Node.js and WebSocket, then make the application suitable for a multi-server environment by adding Redis as a message broker.
Technical Summary
This guide explains how to scale a Node.js-based WebSocket application.
The problem is that chat applications running on a single server cannot communicate with different servers.
The solution is to share messages between servers with the Redis Pub/Sub mechanism.
Preliminary Preparations
Before you start, you need to have the following requirements ready:
- Node.js (v12 and above)
- VS Code or similar code editor
- Redis (local or remote server)
- Basic knowledge of JavaScript, HTML and WebSocket
1. Server Side Installation
1.1 Starting the Project
npm init -y
npm install ws ioredis
- These commands create the Node.js project and install WebSocket and Redis dependencies.
1.2 Creating a Simple HTTP Server
const http = require("http");
const server = http.createServer((req, res) => {
res.end("Merhaba Chat Uygulaması");
});
const PORT = process.argv[2] || 3459;
server.listen(PORT, () => {
console.log(`Sunucu ${PORT} portunda çalışıyor`);
});
- This code starts a basic web server with Node.js' built-in HTTP module.
2. Preparing the HTML Interface
2.1 index.html File
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8" />
<title>Pub/Sub Chat</title>
</head>
<body>
<h2>Pub/Sub Chat Uygulaması</h2>
<div id="mesajKutusu"
style="border:1px solid #ddd;height:300px;overflow-y:auto;padding:10px;">
</div>
<form id="mesajFormu" style="margin-top:10px;">
<input type="text" id="mesajMetni" placeholder="Mesajınızı yazın..." />
<button type="submit">Gönder</button>
</form>
<script>
const socket = new WebSocket(`ws://${location.host}`);
const mesajKutusu = document.getElementById("mesajKutusu");
socket.onmessage = (event) => {
if (event.data instanceof Blob) {
event.data.text().then(yaz);
} else {
yaz(event.data);
}
};
function yaz(text) {
const p = document.createElement("p");
p.textContent = text;
mesajKutusu.appendChild(p);
}
document.getElementById("mesajFormu").addEventListener("submit", (e) => {
e.preventDefault();
const input = document.getElementById("mesajMetni");
socket.send(input.value);
input.value = "";
});
</script>
</body>
</html>
- This file establishes a WebSocket connection and displays messages in the user interface.
2.2 Publish HTML File from Server
const fs = require("fs");
const path = require("path");
const server = http.createServer((req, res) => {
const filePath = path.join(__dirname, "index.html");
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(500);
return res.end("Dosya okunamadı");
}
res.writeHead(200, { "Content-Type": "text/html" });
res.end(data);
});
});
- This code returns HTML interface to requests from the browser.
3. WebSocket Integration
const WebSocket = require("ws");
const wss = new WebSocket.Server({ server });
wss.on("connection", (client) => {
client.on("message", (mesaj) => {
wss.clients.forEach((c) => {
if (c.readyState === WebSocket.OPEN) {
c.send(mesaj);
}
});
});
});
- This structure distributes incoming messages to all clients connected to the same server.
4. Scaling Problem
- Messaging is seamless when a single server is running.
- But when the application grows, more than one server is required.
- At this point, the different servers are not aware of each other.
5. Pub/Sub Architecture with Redis
- Redis acts as a message broker here.
- A server publishes the message to Redis, other servers subscribe to this message.
5.1 Redis Integration
const Redis = require("ioredis");
const redisPublisher = new Redis();
const redisSubscriber = new Redis();
redisSubscriber.subscribe("genel_sohbet");
wss.on("connection", (client) => {
client.on("message", (mesaj) => {
redisPublisher.publish("genel_sohbet", mesaj);
});
});
redisSubscriber.on("message", (kanal, mesaj) => {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(mesaj);
}
});
});
- This code allows messages to be shared between all servers.
5.2 Multi-Server Testing
node app.js 3459
node app.js 3460
- Servers running on different ports share messages via Redis.
Frequently Asked Questions
-
When is Pub/Sub architecture preferred? It is used in real-time and loosely coupled systems.
-
Can there be a database instead of Redis? Technically it is possible, but performance suffers.
-
How many users does this structure support? It depends on Redis and server capacity.
-
Is there a single point error? Redis is available if it works alone.
-
Are there alternative message brokers? RabbitMQ and Kafka are common alternatives.
Result
In this guide, we made a simple chat application with Node.js scalable with the GenixNode architecture. Thanks to Redis, we provided real-time messaging between different servers. This approach forms the basis of modern, high-traffic applications.

