How I Built a Real-Time Chat App Using WebSockets & Node js

WebSocket Meaning

WebSocket is a communication method that keeps a continuous, open connection between a client (like a browser) and a server, allowing both sides to send data to each other instantly whenever needed. In this article, We’ll learn how to build a chat app using websockets and node js.

Easy Way to Understand

Instead of repeatedly asking the server, “Do you have new data?” (like in normal HTTP), WebSocket keeps the connection alive so:

  • The server can push updates instantly
  • The client can send data anytime
  • No need to reconnect again and again

It’s like having a live conversation instead of sending separate letters.

How It Actually Works

  1. The client requests a WebSocket connection
  2. The server accepts it (called a “handshake”)
  3. The connection stays open
  4. Data flows in both directions anytime

Why WebSockets Are Used

WebSockets are useful when you need real-time updates, such as:

  • Chat applications
  • Live notifications
  • Online games
  • Stock price updates

Building a real-time chat app sounds simple… until you actually try it.

I went in thinking, “It’s just sending messages back and forth.”
But I quickly realized: real-time systems introduce a completely different set of challenges—connections, latency, scaling, state, and unexpected edge cases.

Here’s a practical, no-fluff breakdown of what I learned while building one from scratch using WebSockets and Node.js.

Why I Chose WebSockets (and Not HTTP)

Initially, I considered using regular HTTP APIs. But here’s the problem:

  • HTTP is request-response
  • Chat apps need instant, two-way communication

That’s where WebSockets shine.

WebSockets create a persistent connection between client and server
Data can flow both ways instantly

Think of it like an open phone call instead of sending letters back and forth.

Chat App using Websocket: Basic Architecture

Here’s the simple structure I used:

  • Frontend: HTML + JS (or React)
  • Backend: Node.js
  • WebSocket Library: ws (lightweight and fast)

Flow:

  1. User connects → WebSocket connection opens
  2. User sends message → Server receives it
  3. Server broadcasts → All connected users receive it

Chat App using Websocket: Setting Up the WebSocket Server

I started with a minimal Node.js server using ws.

Install dependency:

npm install ws

Basic server:

const WebSocket = require('ws');

const server = new WebSocket.Server({ port: 3000 });

server.on('connection', (socket) => {
    console.log('User connected');

    socket.on('message', (message) => {
        console.log('Received:', message.toString());

        // Broadcast to all clients
        server.clients.forEach(client => {
            if (client.readyState === WebSocket.OPEN) {
                client.send(message.toString());
            }
        });
    });

    socket.on('close', () => {
        console.log('User disconnected');
    });
});

This alone gives you a working real-time chat.

Frontend: Connecting to the Server

Here’s a simple browser client:

<input id="msg" placeholder="Type message..." />
<button onclick="sendMessage()">Send</button>
<ul id="chat"></ul>

<script>
const socket = new WebSocket('ws://localhost:3000');

socket.onmessage = (event) => {
    const li = document.createElement('li');
    li.textContent = event.data;
    document.getElementById('chat').appendChild(li);
};

function sendMessage() {
    const input = document.getElementById('msg');
    socket.send(input.value);
    input.value = '';
}
</script>

At this point, I had a working chat app in under 50 lines.

But that’s where the easy part ended.

The First Real Challenge: Managing Connections

Initially, I didn’t think much about connections. But soon I noticed:

  • Users refresh → new connections are created
  • Old connections may linger
  • Memory usage grows

Lesson:

Always handle disconnects properly.

socket.on('close', () => {
    console.log('User disconnected');
});

And avoid storing unnecessary references to sockets.

Real-Time Chat App: Broadcasting Isn’t Always Enough

At first, I broadcasted messages to everyone.

But real apps need:

  • Private chats
  • Rooms / groups
  • User-specific events

Example: Rooms

const rooms = {};

function joinRoom(room, socket) {
    if (!rooms[room]) rooms[room] = [];
    rooms[room].push(socket);
}

Lesson: Design your message structure early

Example message format:

{
  "type": "chat",
  "room": "general",
  "message": "Hello"
}

Handling JSON Messages (Important)

I made a mistake early on by sending raw strings.

Bad idea.

Always structure your messages:

socket.on('message', (data) => {
    const parsed = JSON.parse(data);

    if (parsed.type === 'chat') {
        // handle chat
    }
});

Lesson: Treat WebSocket messages like API requests.

Real-Time Chat App: Performance Lessons I Learned

1. Avoid Broadcasting Everything

Sending messages to all users doesn’t scale.

Instead:

  • Send only to relevant users (rooms, private chats)

2. Keep Messages Lightweight

Don’t send unnecessary data.

Bad:

{ "user": {...fullProfile}, "message": "Hi" }

Good:

{ "userId": 1, "message": "Hi" }

3. Handle Large Number of Connections

Node.js can handle many connections, but:

  • Each socket consumes memory
  • Too many users → performance drops

Solution:

  • Use horizontal scaling
  • Add load balancer + multiple servers

Scalability: What Breaks First

When I thought about scaling, I hit a big issue:

WebSockets are stateful

That means:

  • A user is connected to one server
  • Other servers don’t know about that connection

Solution: Redis Pub/Sub

I used Redis to sync messages across servers:

Flow:

  1. User sends message → Server A
  2. Server A publishes to Redis
  3. Server B receives → sends to its clients

This makes your chat app scalable.

Mistakes I Made (So You Don’t)

1. Ignoring Error Handling

WebSockets can fail silently.

Always handle errors:

socket.on('error', (err) => {
    console.error(err);
});

2. No Authentication

Initially, anyone could connect.

Fix:

  • Use JWT tokens during connection

3. Sending Too Many Events

Typing events, read receipts, etc. can flood the server.

Solution:

  • Throttle events
  • Batch updates

4. Not Handling Reconnection

Users lose internet → app breaks.

Fix:

  • Auto-reconnect logic on frontend

Real-World Features I Added Later

Once basics were done, I added:

  • Typing indicators
  • Online/offline status
  • Message timestamps
  • Basic chat history (using database)

Key Takeaways

  • WebSockets are powerful but stateful and complex
  • Start simple, then add features gradually
  • Structure your messages properly from day one
  • Scaling requires external systems (Redis, load balancing)
  • Performance depends on how efficiently you send data

Final Thoughts

Building this chat app changed how I think about backend systems.

It’s not just about writing code—it’s about:

  • managing connections
  • handling real-time events
  • designing for scale

If you’re starting out, don’t overcomplicate things.

👉 Build a simple version first
👉 Break it, improve it, scale it

That’s where the real learning happens.

Official Websocket Documentation:

https://websocket.org
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

Read More:

https://bygrow.in/generative-ai-explained/
https://bygrow.in/machine-learning-ai-explained-the-simplest-guide-ever/

Leave a Comment