DEV Community

Cover image for WebSocket broadcasting with JavaScript and Bun
Roberto B.
Roberto B.

Posted on

WebSocket broadcasting with JavaScript and Bun

Broadcasting is one of the most powerful features of WebSockets, allowing servers to send messages to multiple connected clients simultaneously. Unlike point-to-point communication, where messages are exchanged between a single client and the server, broadcasting enables a single message to reach a group of clients. This makes it indispensable for real-time, collaborative, and interactive applications.


Why Broadcasting Is Important

Broadcasting is essential for scenarios where multiple users need to stay synchronized or informed about the same updates in real-time. For example:

  • Group chat applications: sending a message to all participants in a chat room.
  • Collaborative tools: updating all users about shared documents or content changes.
  • Live notifications: broadcasting breaking news, stock updates, or sports scores to multiple subscribers.
  • Online gaming: synchronizing game states or actions across multiple players.

In such cases, broadcasting ensures that all connected users are kept in sync without requiring individual server calls for each client, which would otherwise be inefficient and prone to latency.


Two approaches to broadcasting

When implementing broadcasting, there are two common strategies to consider:

  • Broadcasting to all clients (including the sender)
  • Broadcasting to all clients except the sender

Broadcasting to all clients (including the sender)

This approach sends the message to all clients connected to a specific channel, including the one that originated the message.

This approach is suitable for situations where every client, including the sender, needs to receive the broadcast, such as displaying an acknowledgment or update of their message in a group chat.

Broadcasting to all clients except the sender

In this case, the message is broadcast to all clients except the one who sent it.

This approach is ideal for scenarios where the sender doesn’t need to see their own message in the broadcast, such as a multiplayer game in which actions need to be shared with other players but not echoed back to the one performing the action.

Both methods have specific use cases and can be implemented easily with tools like Bun, allowing developers to handle broadcasting efficiently with minimal code.


This article delves into how to set up WebSocket broadcasting using Bun and demonstrates both broadcasting approaches, helping you build robust real-time applications.

The code for broadcasting with WebSockets

In the first article of this series, WebSocket with JavaScript and Bun, we explored the structure of a WebSocket server that responds to messages sent by a client.

This article will explore channel subscriptions, a mechanism that enables broadcasting messages to multiple clients.

We’ll begin by presenting the complete code and then break it down to explore all the relevant parts in detail.

Create the broadcast.ts file:

console.log("🤗 Hello via Bun! 🐰");
const server = Bun.serve({
  port: 8080, // defaults to $BUN_PORT, $PORT, $NODE_PORT otherwise 3000
  fetch(req, server) {
    const url = new URL(req.url);
    if (url.pathname === "/") return new Response(Bun.file("./index.html"));
    if (url.pathname === "/surprise") return new Response("🎁");

    if (url.pathname === "/chat") {
      if (server.upgrade(req)) {
        return; // do not return a Response
      }
      return new Response("Upgrade failed", { status: 400 });
    }

    return new Response("404!");
  },
  websocket: {
    message(ws, message) {
      console.log("✉️ A new Websocket Message is received: " + message);
      ws.send("✉️ I received a message from you:  " + message);
      ws.publish(
        "the-group-chat",
        `📢 Message from ${ws.remoteAddress}: ${message}`,
      );
    }, // a message is received
    open(ws) {
      console.log("👋 A new Websocket Connection");
      ws.subscribe("the-group-chat");
      ws.send("👋 Welcome baby");
      ws.publish("the-group-chat", "🥳 A new friend is joining the Party");
    }, // a socket is opened
    close(ws, code, message) {
      console.log("⏹️ A Websocket Connection is CLOSED");
      const msg = `A Friend has left the chat`;
      ws.unsubscribe("the-group-chat");
      ws.publish("the-group-chat", msg);
    }, // a socket is closed
    drain(ws) {
      console.log("DRAIN EVENT");
    }, // the socket is ready to receive more data
  },
});
console.log(`🚀 Server (HTTP and WebSocket) is launched ${server.url.origin}`);

setInterval(() => {
  const msg = "Hello from the Server, this is a periodic message!";
  server.publish("the-group-chat", msg);
  console.log(`Message sent to "the-group-chat": ${msg}`);
}, 5000); // 5000 ms = 5 seconds
Enter fullscreen mode Exit fullscreen mode

you can run it via:

bun run broadcast.ts
Enter fullscreen mode Exit fullscreen mode

This code introduces broadcasting, allowing the server to send messages to all subscribed clients in a specific channel. It also differentiates between broadcasting to all clients (including the sender) or excluding the sender. Here's a detailed explanation:


Initializing the server

const server = Bun.serve({
  port: 8080,
  ...
});
Enter fullscreen mode Exit fullscreen mode

The initialization is the same of the previous article.
The server listens on port 8080 and similar to the previous example, it handles HTTP requests and upgrades WebSocket connections for /chat.


Broadcasting in WebSockets

Broadcasting allows a message to be sent to all clients subscribed to a specific channel, like a group chat.
Here’s how the code achieves this:


Subscribing to a channel (in the open Event)

open(ws) {
  console.log("👋 A new Websocket Connection");
  ws.subscribe("the-group-chat");
  ws.send("👋 Welcome baby");
  ws.publish("the-group-chat", "🥳 A new friend is joining the Party");
}
Enter fullscreen mode Exit fullscreen mode
  • ws.subscribe(channel): subscribes the client to the channel the-group-chat. All clients in this channel can now receive messages broadcasted to it.
  • ws.send(...): the client is welcomed individually .
  • ws.publish(channel, message): broadcasts a message to all clients in the channel.

Broadcasting messages (replying/broadcasting a message from a client in the message event)

message(ws, message) {
  console.log("✉️ A new Websocket Message is received: " + message);
  ws.send("✉️ I received a message from you:  " + message);
  ws.publish("the-group-chat", `📢 Message from ${ws.remoteAddress}: ${message}`);
}
Enter fullscreen mode Exit fullscreen mode

When a message is received from a client:

  • The sender gets an acknowledgment via ws.send(...).
  • The message is broadcasted to all clients (excluding the sender) in "the-group-chat" using ws.publish(...).

Note: The sender doesn't receive the broadcast message because we call the publish method on the ws object. You should use the server object to include the sender.


Unsubscribing and broadcasting on disconnect (in the close event)

close(ws, code, message) {
  console.log("⏹️ A Websocket Connection is CLOSED");
  const msg = `A Friend has left the chat`;
  ws.unsubscribe("the-group-chat");
  ws.publish("the-group-chat", msg);
}
Enter fullscreen mode Exit fullscreen mode

When a client disconnects:

  • ws.unsubscribe(channel): removes the client from the channel subscription.
  • A message is broadcasted to all remaining clients in the channel, notifying them of the disconnection.

Periodic server messages to all the clients

setInterval(() => {
  const msg = "Hello from the Server, this is a periodic message!";
  server.publish("the-group-chat", msg);
  console.log(`Message sent to "the-group-chat": ${msg}`);
}, 5000); // 5000 ms = 5 seconds
Enter fullscreen mode Exit fullscreen mode

Every 5 seconds, the server broadcasts a message to all clients in the "the-group-chat" channel using server.publish(...). Here we are using the server object.


Key methods

  • ws.subscribe(channel): subscribes the current WebSocket client to a channel for group communication.
  • ws.publish(channel, message): broadcasts a message to all clients in the specified channel (excluding the sender).
  • server.publish(channel, message): similar to ws.publish, but used at the server level for broadcasting to all the subscribed clients (including the sender).
  • ws.unsubscribe(channel): removes the current client from the channel.

Example flow

  1. A client connects to /chat, subscribing to "the-group-chat".
  2. The client sends a message to the server:
    • The message is echoed back to the sender.
    • The server broadcasts the message to all other clients in the channel.
  3. When a client disconnects:
    • it is unsubscribed from the channel.
    • The server notifies the remaining clients about the disconnection.
  4. Every 5 seconds, the server sends a periodic broadcast message to all clients.

Conclusion

WebSockets are a powerful tool for building real-time, interactive web applications. Unlike traditional HTTP communication, WebSockets provide a persistent, two-way channel that enables instant message exchange between the server and connected clients. This makes them ideal for scenarios like live chats, collaborative tools, gaming, or any application where low-latency communication is crucial.

In this article (and in the series), we explored the basics of setting up a WebSocket server using Bun, handling client connections, and broadcasting messages to subscribed clients. We also demonstrated how to implement a simple group chat system where clients can join a channel, send messages, and receive updates from both other clients and the server itself.

By leveraging Bun’s built-in WebSocket support and features like subscribe, publish, and unsubscribe, it becomes remarkably easy to manage real-time communication. Whether you’re sending periodic updates, broadcasting to all clients, or managing specific channels, WebSockets provide an efficient and scalable way to handle such requirements.

Top comments (0)