PHASE 3 — Topic 11: Sending Data Both Ways — Client to Server and Server to Client

We have already sent data in both directions in earlier examples. In this post, we slow down and look specifically at the different ways data can flow, since Socket.IO gives you more options than a single client just talking to a single server.

The Four Basic Directions

Every real Socket.IO application uses some combination of these four patterns:

  1. Client sends data to the server
  2. Server sends data back to that one specific client
  3. Server sends data to every connected client
  4. Server sends data to every client except the sender

Let's go through each one with real code.

1. Client to Server

This is the simplest direction, the client emits an event, and the server listens for it.

Client:


    socket.emit("sendMessage", { text: "Hello", sender: "Gagan" });

Server:


    socket.on("sendMessage", (data) => {
        console.log(data.text, data.sender);
    });

Inside the connection handler, socket always represents that one specific client's connection, so anything you do with socket only affects that particular user.

2. Server to One Specific Client

If the server wants to reply only to the client that just sent something, it uses socket.emit(...) from inside that same connection handler:


    io.on("connection", (socket) => {
        socket.on("sendMessage", (data) => {
            socket.emit("messageReceived", { status: "delivered" });
        });
    });

Since socket refers to one exact connection, socket.emit(...) here only reaches that single client, nobody else on the server gets this event.

3. Server to Every Connected Client

Sometimes you want everyone to see an update, for example, a new chat message that all users should see. For this, you use io.emit(...) instead of socket.emit(...):


    io.on("connection", (socket) => {
        socket.on("sendMessage", (data) => {
            io.emit("newMessage", data);
        });
    });

Here, io represents the entire server, not one connection. io.emit(...) sends the event to absolutely every client currently connected, including the one who originally sent the message.

4. Server to Everyone Except the Sender

Often, you don't want to send an update back to the same person who triggered it, they already know, since they're the one who did it. For this, Socket.IO gives you socket.broadcast.emit(...):


    io.on("connection", (socket) => {
        socket.on("sendMessage", (data) => {
            socket.broadcast.emit("newMessage", data);
        });
    });

This sends the event to every client except the one represented by socket. This is extremely common in chat apps, you show the sender their own message instantly through local state in React, and use broadcast so the server does not also send it back to them separately.

A Very Common Real Pattern

Combining points 2 and 4 together is one of the most useful patterns you will use repeatedly:


    socket.on("sendMessage", (data) => {
        socket.emit("messageSent", { status: "ok" });
        socket.broadcast.emit("newMessage", data);
    });

Here, the sender gets a private confirmation that their message was received, while everyone else gets the actual new message. This avoids duplicate messages appearing for the sender, while still keeping everyone in sync.

Sending to a Specific Client by ID

Sometimes you don't have direct access to a client's socket object, but you know their socket ID (perhaps you stored it earlier, tied to a user account). In that case, you can target them directly:


    io.to(socketId).emit("privateNotification", { text: "You have a new order" });

This sends the event only to the client with that exact socket ID, useful for private notifications, direct messages, or admin actions targeting a specific user.

A Quick Reference Table

Code

Who Receives It

socket.emit(...)

Only this one client

io.emit(...)

Every connected client, including sender

socket.broadcast.emit(...)

Every client except the sender

io.to(socketId).emit(...)

Only the client with that specific socket ID

Client Side: Listening for Both Kinds of Events

On the client, you don't need to know or care which method the server used, you just listen normally:


  useEffect(() => {
    function onMessageSent(data: { status: string }) {
      console.log("My message status:", data.status);
    }

    function onNewMessage(data: { text: string; sender: string }) {
      console.log("New message from someone:", data);
    }

    socket.on("messageSent", onMessageSent);
    socket.on("newMessage", onNewMessage);

    return () => {
      socket.off("messageSent", onMessageSent);
      socket.off("newMessage", onNewMessage);
    };
  }, []);

Why This Matters for Real Apps

Understanding these four directions properly prevents two very common beginner mistakes:

  • Using io.emit(...) everywhere, which accidentally sends private data (like personal notifications) to every connected user instead of just one
  • Using socket.emit(...) for something meant to update everyone, which means other users never see the update in real time at all

Choosing the right one of these four patterns for each event is really the core skill of designing a Socket.IO application properly.

Summary

  • socket.emit(...) sends data to only the one client tied to that connection
  • io.emit(...) sends data to every connected client, including the sender
  • socket.broadcast.emit(...) sends data to every client except the sender, extremely common in chat-style features
  • io.to(socketId).emit(...) targets one specific client by their socket ID
  • Picking the correct direction for each event is essential to avoid data going to the wrong people, or not reaching the right people at all

In the next post, we cover handling connection and disconnection events properly, including cleanup logic and tracking online users.

No comments:

Post a Comment

PHASE 4 — Topic 13: Building a Simple Real-Time Chat Application (UI with Tailwind)

Time to put everything from Phase 3 together into one complete, working feature: a real chat application. This post combines connection hand...