Server Sent Events with Typescript and Redux Saga

Mohit Popli
8 min readJan 17, 2021

Today I am going to talk about Server Sent Events(SSE) that provides a powerful mechanism to communicate with server and allows us to have real time updates. Last few days I was implementing this in one of the project and I found very less information about it’s implementation using React and Sagas as middleware. In this article, I will talk about SSE and it’s practical use cases and will design a basic demo application to display server time using SSE.

Now you guys must be wondering why I have added this “One way” traffic sign board!! Well this is what Server Sent Events means and are used for.

Server Sent Events provides uni-directional way that helps client side application to communicate to server. In other words server streams updates to client in real time. To understand more about it let’s understand why they came into picture and what is the main benefit of using it in application.

Over past few years, new protocol have arrived named as Web Sockets but this is only useful when both client and server wants to communicate with each other using event streams.

Idea behind SSE was similar i.e. client establishes connection to stream of updates generated by server and when server gets new update it notifies client to perform necessary actions. Now its predecessors such as AJAX has some limitations such as POLLING and LONG POLLING

Polling refers to the normal technique where client initiate a request to server using fetch api and waits for server to respond with data or empty response. It creates overhead as we are sending HTTP requests regularly.

Long polling is another variation of Polling where server holds the request if it does not have data and then responds back when new data is available. When new data is fetched server sends data to client and closes the connection. This process is repeated again. This is also known as Hanging Get. This process involves certain hacks to fetch new updates.

These give birth to SSE’s. As I explained earlier, using this technique Server can push updates to client without any hack or extra request in real time. Another benefit of using SSE’s is they’re handled by your browser and all you need to do is listen to the updates and act accordingly. Isn’t this interesting!!

Now we will design a simple application to see SSE’s in action. Little knowledge about NodeJs, Typescript, Saga middlewares are required. But I’ll try to keep it simple and explain as much as I could.

Let’s start by creating a demo app using CRA

yarn create react-app server -sent-events-demo— template typescript

For this demo app we need to install certain dependencies,

“connected-react-router”: “6.5.0”,
“eventsource”: “1.0.7”,
“history”: “4.9.0”,
“immer”: “8.0.0”,
“react-dom”: “17.0.1”,
“react-redux”: “7.1.2”,
“react-router”: “5.2.0”,
“react-router-dom”: “5.2.0”,
“redux”: “4.0.4”,
“redux-logger”: “3.0.6”,
“reselect”: “4.0.0”,
“styled-components”: “5.2.1”,
“typesafe-actions”: “5.1.0”,

Once you install these dependencies, you’re ready to code and design a simple application to show time. Now we need to configure our project to connect redux and saga middlewares along with connected-react-router. I’m skipping those steps as I’ll be providing repo link for you guys to configure it easily.

Let’s start with eventsource that is heart of this entire article and will help to connect to our custom NodeJs server that I’ll show you guys later in this article.

const eventSrc = new EventSource(‘http://127.0.0.1:5000/events')

The above line will open a stream to receive updates from server, in my case it will be simple node server but you can also establish connection with your API. This will also provide you options to add optional parameters such as Authentication headers etc.

Next step will be to add event listeners for your stream, by default it provides onopen, onmessage, onerror events to subscribe. But you can add your custom event listeners using addEventListener method. We’ll see this in a while.

Now I will create 2 components home.tsx and child.tsx.

Home.tsx is the parent component of child.tsx. Once we load child.tsx it will dispatch action to store and saga will listen to this action and establish connection with my node server to listen to events.

home component

child component

Now as you can see once this child component is loaded it dispatches action to subscribe to event stream and when it will unmount it will unsubscribe to event stream.

child saga.ts

In the above file you can see in subscribe saga we’re opening an event stream and then saga will call subscribeToSSE utility that I’m going to show it to you guys to receive updates from server. Once it receives updates it informs child component to update time received from server by dispatching another action.

SubscribeToSSE utilility

This is the core utility that will handle events from the stream that we opened to accept updates from our node server. Here I used eventChannel provided by redux-saga to listen to external event channel and broadcast event to saga who’s listening to this channel. Notice one thing we have to provide unsubscribe function which will be called when we close the channel and we can dispatch END event from redux-saga to inform everyone listening to this channel that it’s closed and will not be dispatching any further updates.

Now once we establish the connection, we’ll receive “open” event and in case something goes wrong due to CORS or server isn’t responding we will receive “error” event. Other than this as I mentioned earlier if we want to send specific data event we can add custom listener to listen to particular event when we receive it from server through the open stream. Normal data events can be listened by adding

eventSrc.onMessage =(ev:any) => console.info(ev) // will print data

Now after setting up the client application, we also need to setup our node server to see this in action. For people who already have API set up, all you need to do is add your url in EventSource constructor and you’re all set to receive events from the stream once you set up the project as per my repo.

Now let’s understand Event Stream Format first,

data: <Your message>\n\n

This is simplest form in which your server can send data through event stream and you’ll receive it atonMessage handler.

If you wish to send multiline data, you can also split your data into multiple lines such as

data: <Message 1>\n
data: <Message 2>\n\n

At client side you’ll see it as Message1\nMessage2 You can split this at \n to get your multiline message.

Now let’s talk about use case which will be used in many application as we know backend API’s love to send data in JSON format right 😄, let’s see how we can achieve that. In above example, I actually showed you to handle JSON data in saga once we receive it from server as my server is sending JSON data.

res.write("event: child_updated\n");
res.write('id: ' + id + '\n');
res.write(`data:{ "time": ${JSON.stringify(data)} }`)
res.write("\n\n");

In the above code I’m sending specific event using event attribute. This is the way how you send named events and listen to them at client side by adding listeners for them. Under data attribute you can see I’m sending JSON data to my client where it will use JSON.parse to parse my data.

One important thing I need to mention if you guys have noticed that I’m sending id in my event. What is this!! 🤔

Let’s understand this. What happens when connection abrupts? And your event updates are necessary for client to have them all and client cannot afford to miss any event. Here this id attribute comes into picture. Remember I told you browser handles SSE and all client needs to do is listen to updates.

This id attribute helps browser to keep track of the events fired, so that when connection to server drops, a special HTTP header last-event-id is sent with the new request. At server you can then act accordingly and this lets browser to fire appropriate event. Your event will contain ev.lastEventId property.

This is cool!! Browser is taking care of everything!!! 😎

Now another question is coming to my mind, we know connection can be disrupted right, so when to reconnect? Who helps us doing this? Of course you know browser right but what’s the default timeout? Can you configure this as per your requirements?

Answer is YES ✌️!!

retry: 5000\n

This is another attribute that you can send in your event from server to inform browser when to reconnect once connection is disrupted. If this is not mentioned then default connection timeout is around 3s after connection is closed.

Now let’s create our Node server and run this application to see this in action. I cannot wait anymore…!!

I have created a very simple http server to add necessary details to open a stream for my react application to receive time and display it in child.tsx file.

Points to remember:

  • Content-type in headers should be text/event-stream
  • Connection header should be keep-alive so that browser will not close the stream
  • Don’t forget to conform to CORS policy by adding CORS header

So the above server that we created will send updated time after every 5s to client and it’s sending data with named event “child_updated” and time as JSON data to client.

Create this server.js file at the same level where your react application resides by creating a new folder and adding server.js file inside that folder.

To run server open the terminal and run node server.js

Make sure you’ve node installed otherwise you can download NODE and install it.

Now let’s see it in action,

Now once you see the demo, you will notice once we navigate to child component it opens event stream to my already running node sever to get time updates after every 5s. I have shown you the event format that we receive from node server.

Once we navigate away from child, you see how we closed the event stream and we no longer receive event updates from server. For demo purpose, I am sending events after every 5s from server but in your case server can receive updates from another API or Kafka.

As promised you can find this application in my repo.

I hope it will help you to understand beauty of SSE and easy to integrate with your React app.

Let me know if you have any questions in comments. Always happy to help!! ✌️🙂

--

--

Mohit Popli

Software Engineer @Malaysia | Innovator | @twitter m_popli5 | @insta m.popli5