Server Sent Event is nothing but receiving data from the server through the event stream. This is one-way communication and only the server can push data to the client. Whereas WebSocket works two-way & can send an event with payload to the server as well as server can trigger an event to the client.
EventSource is the class used to open an event stream from the server. We need to create an object of EventSource class and pass the API url inside the constructor. After that just subscribe to the event that is triggered from the server. If there is no custom event triggered by API then the message is the default event that can be subscribed.
Check below the example.
let eventStream = new EventSource(`${URL}?city=${selectedCityValue}&sec=${secondsToUpdate.value}`);
eventStream.addEventListener('open', () => {
console.log("Connected and subscribed to channel ");
});
eventStream.addEventListener('UPDATE', (event) => {
const data = JSON.parse(event.data);
console.log('eventStream data', data);
time.innerHTML = data.dateTime;
});
eventStream.addEventListener('error', (err) => {
console.error("EventSource failed:", err);
});
You can send query param to API URL to make partially two-way communication 🙂 And whenever you want to send or update query param just close this previous connection and open another.
Close the connection using a close method provided by the event source object.
if(eventStream) {
eventStream.close();
}
I don’t have much experience with Backend technologies but created a simple node server with express for example purposes.
const express = require('express');
const app = express();
const MAX_LIMIT = 100;
function getFormattedDate() {
const d = new Date();
return `${d.getDate()}/${d.getMonth()+1}/${d.getFullYear()} ${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}`;
}
app.get('/event-stream', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Access-control-Allow-Origin', '*');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
const sec = (req.query && req.query.sec) ? req.query.sec : 5
const timezone = (req.query && req.query.city) ? req.query.city : 'Asia/Calcutta';
let count = 0;
process.env.TZ = timezone;
console.log('Request Param -> ', timezone, ' seconds -> ', sec);
let interval = setInterval(() => {
count++;
if(count >= MAX_LIMIT) {
console.log('Interval Reached Max Limit Closing the Connection');
clearInterval(interval);
res.end();
return;
}
const obj = {
id: count,
dateTime: getFormattedDate()
};
const time = new Date().getTime();
res.write(`id: ${time}_${count}\n`);
res.write(`event: UPDATE\n`);
res.write(`data: ${JSON.stringify(obj)}\n\n`);
}, sec * 1000);
req.on('open', (event) => {
console.log('connection open ', event);
});
res.on('close', () => {
console.log('closed connection');
clearInterval(interval);
res.end();
});
});
app.listen(3000, () => {
console.log('listening on port 3000');
});
Check the live example to display the current server time using SSE. (Full Source Code)
The limitation of SSE is browser can open only six connections and this limit is per browser with the domain. This limitation is there only when using HTTP/1, not for HTTP/2 has increased this limit.
The practical and real scenario where we can take advantage of SSE is when there is a large dataset that needs to be processed on the server side and we don’t know exactly when that process will complete. There are two solutions to solve this problem.
Send a REST API request to the server to start processing then start pulling the status from the server via API to check process is completed or not.
Send a REST API request to the server to start the process and then create an event source object with some unique process id as a query param, once the process is completed then just dispatch the action from the server. Second way is more cleaner than pulling status from server every some time.
Please check the live example that can demonstrate this scenario. (Source Code)
Top comments (1)
Very well explained!