Real-time Updates: Polling, SSE and Web Sockets
Tags: javascript, webdev, codenewbie, frontend
Published: 2022-01-17 20:04:42

Cover image by [Martin Jaros](https://unsplash.com/@devoonek) on [Unsplash](https://unsplash.com).

Hey 👋

If you are a junior or mid-level front-end developer 👨‍💻, chances are you might have implemented something that needs real-time updates. Maybe a notification system, a chat-application, an upload progress bar or social media status indicator.

Making a chat app is on everyone's to-do list. I know that because I have been through that phase too. I built one while following a YouTube tutorial. 🤷‍♂️ The same tutorial that has been uploaded by almost every YouTube channel out there: using [socket.io](https://socket.io/).

![Mr. Bean Copying](https://c.tenor.com/OtR5vp1C3QcAAAAd/fb-f-band-i-gdown.gif)


Did you know that socket.io uses Web sockets under the hood? Yeah, you probably know this. But are real-time updates only possible with web sockets? 🤨 No. This is not the only way. There are few other methods as well that we'll be looking at in this article. 👇

We'll be covering three techniques/technologies:
1. Polling + Long Polling
2. Server Sent Events
3. Web Sockets (in brief)

I've tried my best to explain these with illustrations. 🎨


## Polling
This is the simplest approach to follow when building a real-time app. 

In polling, client makes the request to the server repeatedly in hope for updated/new data. No extra steps are required to achieve this. Just wrap your API call with `setInterval` and you're done. In layman's terms, It's like refreshing your web page every time after few seconds.


![Polling Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otex8996cuf04vix0ekl.png)


Maybe you receive updated data or maybe not. There's no way to know this beforehand.

```javascript
const URL = 'https://jsonplaceholder.typicode.com/posts/';

const fetchPosts = async () => {
  try {
    console.log('Fetching new data...');
    
    const response = await (await fetch(URL)).json();
    
    console.log('Data fetched!')
    
  } catch(err) {
    console.log('Request failed: ', err.message);
  }
}

setInterval(fetchPosts, 5000);
```

### Long Polling
Since we're on this topic it's worth talking about Long Polling here. Long Polling is the genius/optimized version of polling.

Instead of sending a response immediately, server waits until it has some new data for client. Client starves for a response; which is actually fine because client is not blocked and keeps on doing other tasks. It's understood here that this requires some effort on server-side as well.


![Long Polling Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t8ytkba4mqlfaq6jxu2k.png)


Once the client receives the data, it has to create another request for the next state of data.

```javascript
const URL = "https://jsonplaceholder.typicode.com/posts";

const fetchPosts = async () => {
  try {
    console.log("Fetching new data...");

    const response = await (await fetch(URL)).json();

    console.log("Data fetched!");

    return response;
  } catch (err) {
    console.log("Request failed: ", err.message);
  }
};

const longPoll = async () => {
  // response might be delayed as server might not have updated data
  const response = await fetchPosts();

  if (response) {
    return longPoll();
  }

}

longPoll();

```

**Note:** These snippets provide the bare minimum just to convey the idea. You might wanna add more functionalities to this like attempts count or delay. It'd also be nice to add some checks in your code so you don't end up DOSing your own server. 💩


## Server Sent Events
This is my favorite part of this article. I recently learnt about SSE while working at [Syfe](https://www.syfe.com/)(we're hiring!). Before these I only knew about web sockets and used those, even for small apps. SSE are powerful, simple and get the job done with minimal code. 👌

In SSE, client makes the initial request to the server to set up a connection. Post that server pushes updated data to client whenever it is available. No further engagement is required from client. Of course client needs to handle those events but that's it.


![Server Sent Events Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnwuewslr0ohvwujwici.png)


```js
// server-side code in express

app.get("/real-time-updates", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");

  const sendRealTimeUpdates = () => {
    res.write("data: New data!\n\n");
    setTimeout(sendRealTimeUpdates, 3000);
  };

  sendRealTimeUpdates();
});

```

This is the shortest implementation possible. 
1. We created a `GET` route `/real-time-updates`.
2. Set the `Content-Type` header as `text/event-stream`.
3. Use `res.write()` to send data to client. If we use `res.send()` or `res.end()` it will close the connection.

👉 **Important points to note**
1. The message should always start with `data: `. Yes! space too.
2. The message should always end with `\n\n`.

We've simulated real-time updates by wrapping `res.write` with a `setTimeout`.

```javascript
// client-side code in vanilla JS

const URL = 'http://127.0.0.1:3000/real-time-updates';

const sseClient = new EventSource(URL);

sseClient.onopen = () => console.log('Connection opened!');

sseClient.onmessage = (event) => console.log(event.data);

sseClient.onerror = () => console.log('Something went wrong!');

```

We use `EventSource` interface for setting up a connection with SSE end point. 

1. Get an instance of client using `EventSource`. Pass the URL you want to subscribe to.
2. We get 3 event handlers that are called a different stages.
 - `onopen` is called when the connection opens.
 - `onerror` is called when an error occurs.
 - `onmessage` is called when we receive an event from server and we don't handle that event explicitly.
3. We also get a `close` method that can be used to close the connection anytime.

If we don't specify an event type on the server, by default, every event has the type `message`. Hence, the handler `onmessage` which catches every event.

But if we specify an event using `event: ` keyword we can handle it explicitly on the client.

```javascript
// diff: server-side code with custom event

res.write("event: notification\ndata: New data!\n\n");
```

```javascript
// diff: client-side code with custom event handling

sseClient.addEventListener('notification', (event) => {
    console.log(event.data))
};
```

That's all the code you need to add your own SSE. 🤓

⚠️ When SSE is implemented over HTTP/1.1, it suffers from a limitation of maximum number of connections; which is 6. That means any website `www.fake-dev.to` can open upto 6 SSE connections in a browser(multiple tabs included). It's advised to use HTTP/2, which has default limit of 100, but can be configured.


## Web Sockets
Web Sockets are more powerful than the above mentioned methodologies but they come with additional complexities. 

Web Sockets form a duplex connection, which means both client and sever can send data to each other on a single channel whereas SSE are uni-directional.

Web Sockets are initiated by an HTTP request for hankshake but later they are upgraded to TCP layer.


![Web Sockets Illustration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6tbkgcfisr2ccf3eqzxj.png)

HTTP protocol is a stateless protocol, which means all the headers including cookies, tokens, etc. are sent with every request. This makes it horizontally scalable. If server 1 is over loaded, request can be handled by server 2 and since we have all the information in the headers it would make no difference. This also makes it slow, as more data needs to be sent with every request. Also, the connection is closed as soon as request is fulfilled. So, for a new request the connection has to be opened again which is time consuming.

On the other hand, TCP is stateful. Web sockets are faster because connection is kept alive for communication and no additional headers are sent with each request. But this is also makes it a bit harder to scale. If the client was talking to server 1 then all the requests should be handled by server 1 only. Other servers are not aware about it's state.

---

![You after reading this article](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zxh3u9q9utb0qx0ificr.jpg)


With all this being said, there is no perfect solution. Depending on the use case one might be better than other but it's good to know all the possible alternatives. 💪

💡 There's a new API under development known as [WebTransport](https://web.dev/webtransport/) based on HTTP/3, which is meant for low latency, bi-directional, multiplexed, client-server messaging.

📌 Save this for later.

That's all folks! Thank you for reading. :pray:

---

I hope you liked this article. If you like to see content related to front-end development, you can connect with me on [LinkedIn](https://linkedin.com/in/thesanjeevsharma) or [Twitter](https://twitter/thesnjvsharma).

🕊