Real-time Chat Backend (WebSocket)
Part of the Backend Developer Roadmap
In short
A chat backend that works on one server is a tutorial. One that keeps rooms, history and online status working across many servers is a portfolio project.
A more complete real-time backend than the earlier practice project — supporting multiple rooms, message history, presence, and horizontal scaling via Redis pub/sub.

Think of it like this
Walkie-talkies in the hostel
Once everyone's walkie-talkie is on the same channel, anyone can talk and everyone hears it instantly, no dialling each time. A WebSocket is that always-open channel between each phone and your server.
Watch it happen
Riya messages Kabir, who's on another server
message → room cse
1/6Riya's phone sends the message over its open WebSocket to Server A.
The big ideas
What you need to know
Rooms and history
Users join rooms. Messages are saved, and older history loads with cursor pagination as you scroll up.
Presence
Track who's online with heartbeats and short Redis TTLs, so a dropped phone goes offline on its own.
Scale with pub/sub
Sockets stick to one server. A Redis pub/sub adapter lets a message reach users on every server.
Delivery and acks
Acknowledge saved messages and let clients retry with a client-generated ID, so retries don't duplicate.
See it in code
Walk through the code
1import { Server } from "socket.io";2import { createAdapter } from "@socket.io/redis-adapter";3import { createClient } from "redis";4 5const pub = createClient({ url: process.env.REDIS_URL });6const sub = pub.duplicate();7await Promise.all([pub.connect(), sub.connect()]);8const io = new Server(3000, { adapter: createAdapter(pub, sub) });9io.on("connection", (socket) => {10 socket.on("join", (room: string) => socket.join(room));11 socket.on("message", async ({ room, text }, ack) => {12 const saved = await saveMessage(room, socket.data.userId, text); // DB first13 io.to(room).emit("message", saved); // reaches sockets on every server14 ack({ id: saved.id });15 });16});1/3Two Redis connections, one to publish and one to subscribe, power the adapter.
Check that the user is allowed in a room before socket.join, and authenticate the socket during the handshake.
Save yourself the pain
Common mistakes
Oops
Running two servers without an adapter and wondering why some messages vanish.
Do this instead
Each server only knows its own sockets. Add the Redis adapter, or users on different servers can't hear each other.
Oops
Trusting the userId the client sends inside each message.
Do this instead
Authenticate once at connection time, store the user on socket.data, and use that.
Your mission
Milestone 1: two servers, one chat
Quick check
Riya is connected to Server A, Kabir to Server B. Without a pub/sub adapter, what happens when Riya messages their room?
Go deeper
- 1Scale it across multiple server instances using a Redis pub/sub adapter.
- 2Add message delivery guarantees — persist messages before broadcasting.
- 3Support both one-to-one and group conversations.
More projects at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.