Stop 14 · Real-time CommunicationProjects · 1/2

Real-time Chat App

Part of the Backend Developer Roadmap

In short

A chat app looks simple and secretly touches everything: sockets, auth, rooms, a database, ordering and scale. Building one teaches more backend than ten CRUD apps.

A multi-user chat application with rooms — the definitive real-time project, exercising WebSockets, rooms/namespaces, and message persistence.

The blob at a chai stall bench sends blank speech bubbles from a laptop to friends' phones.

Think of it like this

Running the chai-adda group

Your friend circle at the chai stall has groups, side conversations, someone always typing and someone who joins late asking 'what did I miss?'. Your app needs the same: rooms, live delivery, typing hints and saved history for latecomers.

Watch it happen

Build plan, one layer at a time

Sockets
Auth
Rooms
History
Typing
Scale

1/6Start with a Socket.io server and a page that sends and shows messages for everyone.

The big ideas

What you need to know

Save, then send

Write the message to the database first and broadcast the saved row, so every client sees the same id and timestamp.

Acknowledge sends

Use Socket.io acknowledgements so the sender knows the message was saved, and can show a tick or retry.

Paginate history

Load older messages with a cursor like before=messageId, not by page number, so new messages don't shift pages.

Validate everything

Check room membership, message length and type on the server. The client is not your bouncer.

See it in code

Walk through the code

ts
1io.on("connection", (socket) => {2  socket.on("message:send", async ({ roomId, text }, ack) => {3    if (typeof text !== "string" || !text.trim() || text.length > 2000) {4      return ack({ ok: false, error: "Invalid message" });5    }6    const msg = await db.message.create({7      data: { roomId, text: text.trim(), userId: socket.data.userId },8    });9    io.to(roomId).emit("message:new", msg);10    ack({ ok: true, id: msg.id });11  });12});

1/3Reject empty or huge messages and tell the sender why through the ack callback.

socket.data.userId is set in your auth middleware on connect. Add a membership check for roomId before saving in the real app.

Save yourself the pain

Common mistakes

Oops

Broadcasting first and saving later 'for speed'.

Do this instead

If the save fails, people saw a message that doesn't exist. Save, then broadcast.

Oops

Sending the whole chat history on every connect.

Do this instead

Send the latest 50 and fetch older ones on scroll with a cursor.

Your mission

Milestone 1: one room that remembers

~45 min

Quick check

Two users send messages at the same moment. What gives every client the same order?

Go deeper

  1. 1Implement rooms so users can join specific conversations.
  2. 2Persist message history so a reconnecting user sees recent messages.
  3. 3Add typing indicators and online/offline presence for extra polish.

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.

Download
InsideEdge