Skip to content

Latest commit

 

History

History
232 lines (149 loc) · 8.72 KB

channels.md

File metadata and controls

232 lines (149 loc) · 8.72 KB

Channels

This section covers the usage of channels with Better SSE. You should read the Getting Started guide first if you haven't already.

Guide

Introduction

Channels are an abstraction that make it easy to broadcast events to many clients at once.

When a client connects, you first register (subscribe) the session to one or many channels, and from then-on any events broadcast on those channels will also be sent to that client.

Don't worry about deregistering the session from the channel; that is automatically handled for you when the session disconnects, but you can also do it manually if you no longer wish to listen for events on a channel at any time.

Channels can be used for things such as notification systems, chat rooms and synchronizing data between multiple clients.

Create a channel

We are going to be making a channel that does two things:

  1. Synchronizes a number that counts up every second with all clients, and,
  2. Sends a live updating count of how many users are currently online in real time.

Let's start off from where the Getting Started guide finished as a base:

// server.ts
import express from "express";
import { createSession } from "better-sse";

const app = express();

app.use(express.static("./public"));

app.get("/sse", async (req, res) => {
	const session = await createSession(req, res);

	session.push("Hello world!");
);

app.listen(8080);

Right now we have a simple mechanism where a client connects and receives an event with the data "Hello world!".

This is nice, but what if we want to say hi to everyone at once? Or in a more real-world example, send live updates about real-time events in our system to groups of our users? This is where we can use channels.

To make a channel, you simply need to call the exported createChannel() factory function.

Let's create a channel called ticker in a new file and export it:

// channels/ticker.ts
import { createChannel } from "better-sse";

const ticker = createChannel();

export { ticker };

Then import the channel to where your route handler is located:

// server.ts
import { ticker } from "./channels/ticker";

You then need to register the session with your new channel so that it can start receiving events. Inside your route handler add the following just after you create your session:

ticker.register(session);

New sessions will now be subscribed to your channel and will start receiving its events!

Channels are powerful in that you can register your session to listen on many channels at once or none at all. This makes it dynamically configurable based on what channels your client may or may not be authorized to receive events from, and allows you to have a lot of flexibility in your implementation.

Create a counter

Now that you have a channel and your sessions are registered with it, lets actually make it do something.

We are going to have a number that increments by one (1) every second and synchronize it across all of our connected clients in real time.

Back in your ticker.ts file, right after your create your channel, add the following:

let count = 0;

setInterval(() => {
	count = count + 1;

	ticker.broadcast(count, "tick");
}, 1000);

Here we have a variable count that gets incremented by 1 every 1000ms (one second).

We then broadcast the value of count on the ticker channel every interval under the event name tick which will be received by all of our registered sessions.

Back on our client-side let's write some code that updates a text field on the page with the received value:

// public/client.js
const countElement = document.createElement("pre");
document.body.appendChild(countElement);

const eventSource = new EventSource("/sse");

eventSource.addEventListener("tick", ({data}) => {
	countElement.innerText = `The clock has ticked! The count is now ${data}.`;
});

In this snippet the following is happening:

  1. We create a pre element stored in a variable countElement and add it to our document body.
  2. We create an EventSource to listen to events from our server.
  3. We listen for the tick event and, when received, set the text in countElement to display the received value, corresponding to the count variable on the server.

Open it up in your browser (you can run the pre-made example project if you haven't been following along) and you will now see your ticking counter being updated in real time!

You can open the same page in multiple tabs at once and notice that the value is kept in sync across them all. Easy!

Track online users

Let's add some more functionality to our ticker channel. This time we want to keep our users in the know about how many other users are on the site at the same time as them. No one wants to feel lonely!

Channels emit events when certain things happen such as sessions being registered, deregistered and being disconnected. Let's listen on these events to broadcast the total number of connected sessions at any given time.

In our ticker.ts file, after you create your channel, add the following:

const broadcastSessionCount = () => {
	ticker.broadcast(ticker.sessionCount, "session-count");
};

ticker
	.on("session-registered", broadcastSessionCount)
	.on("session-deregistered", broadcastSessionCount);

Here we create a function broadcastSessionCount that broadcasts a value with the current total session count exposed to us under the Channel sessionCount property with the event name session-count.

We then listen on both the events session-registered and session-deregistered and set the broadcastSessionCount function as a callback for each. This way, every time a session joins or leaves the channel the count is re-broadcasted and updated for all of the existing sessions on the channel.

Back on our client lets add another listener for our new event that displays the session count in another text field:

// public/client.js
const sessionsElement = document.createElement("pre");
document.body.appendChild(sessionsElement);

eventSource.addEventListener("session-count", ({data}) => {
	sessionsElement.innerText = `There are ${data} person(s) here right now!`;
});

We do a similar thing to our previous example:

  1. Create a pre element stored in a variable sessionsElement and add it to our document body.
  2. Listen for the session-count event and, when received, set the text in sessionsElement to display the received value, corresponding to the number of active sessions connected.

Once again open the page in your browser (or run the example project) and you will now see a new text element with a real-time updating display of the active sessions. Open and close more tabs on the same page and observe how the count changes to stay in sync. Amazing!

Your finished code should look like the following:

// server.ts
import express from "express";
import { createSession } from "better-sse";
import { ticker } from "./channels/ticker";

const app = express();

app.use(express.static("./public"));

app.get("/sse", async (req, res) => {
	const session = await createSession(req, res);

	ticker.register(session);
});

app.listen(8080);
// channels/ticker.ts
import { createChannel } from "better-sse";

const ticker = createChannel();

let count = 0;

setInterval(() => {
	ticker.broadcast(count++, "tick");
}, 1000);

const broadcastSessionCount = () => {
	ticker.broadcast(ticker.sessionCount, "session-count");
};

ticker
	.on("session-registered", broadcastSessionCount)
	.on("session-deregistered", broadcastSessionCount);

export {ticker};
// public/client.js
const eventSource = new EventSource("/sse");

const sessionsElement = document.createElement("pre");
document.body.appendChild(sessionsElement);

eventSource.addEventListener("tick", ({data}) => {
	countElement.innerText = `The clock has ticked! The count is now ${data}.`;
});

const countElement = document.createElement("pre");
document.body.appendChild(countElement);

eventSource.addEventListener("session-count", ({data}) => {
	sessionsElement.innerText = `There are ${data} person(s) here right now!`;
});

Keep going...

Check the API documentation for information on getting fine-tuned control over your data such as managing event IDs, data serialization, streams, dispatch controls and more.

You can also see the full example from this guide in the examples directory.