Jasch1
Jasch1

Reputation: 573

Simple Way to Implement Server Sent Events in Node.js?

I've looked around and it seems as if all the ways to implement SSEs in Node.js are through more complex code, but it seems like there should be an easier way to send and receive SSEs. Are there any APIs or modules that make this simpler?

Upvotes: 32

Views: 48084

Answers (7)

toddmo
toddmo

Reputation: 22416

After looking at the other answers I finally got this working, but what I ended up having to do was a little different.

[package.json] Use express-sse:

The exact version of express-sse is very important. The latest tries to use res.flush(), but fails and crashes the http server.

"express-sse": "0.5.1",

[Terminal] Install express-sse:

npm install

[app.js] Use the router:

app.use(app.baseUri, require('./lib/server-sent-events').router);

[server-sent-events.js] Create sse library:

The call to pause() is the equivalent of flush(), which was removed from express. It ensures you'll keep getting messages as they are sent.

var express = require('express');
const SSE = require('express-sse');
const sse = new SSE();

var router = express.Router();
router.get('/sse', sse.init)
module.exports = {
    send,
    router
};


async function send(message) {
    sse.send(message.toProperCase(), 'message');
    await pause();
}

function pause() {
    return new Promise((resolve, reject) => {
       setImmediate(resolve)
    })
 }

[your-router.js] Use the sse library and call send:

var express = require('express');
var serverSentEvents = require('../lib/server-sent-events');

var router = express.Router();
router.get('/somepath', yourhandler);
module.exports = router;

async function yourhandler (req, res, next) {
    await serverSentEvents.send('hello sse!'); // <<<<<
}

[your-client-side.js] Receive the sse updates:

I recommend you keep the event.data.replace(/"/g,'') because express-sse tacks on enclosing quotes and we don't want those.

const eventSource = new EventSource('http://yourserver/sse');

eventSource.onmessage = function(event) {
    document.getElementById("result").innerHTML = event.data.replace(/"/g,'') + '...';
  };

Upvotes: 0

codeKonami
codeKonami

Reputation: 960

I'm adding a simple implementation of SSE here. It's just one Node.js file.

You can have a look at the result here: https://glossy-ox.glitch.me/

const http = require('http');
const port = process.env.PORT || 3000;

const server = http.createServer((req, res) => {
  // Server-sent events endpoint
  if (req.url === '/events') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      ...(req.httpVersionMajor === 1 && { 'Connection': 'keep-alive' })
    });

    const refreshRate = 1000; // in milliseconds
    return setInterval(() => {
      const id = Date.now();
      const data = `Hello World ${id}`;
      const message =
        `retry: ${refreshRate}\nid:${id}\ndata: ${data}\n\n`;
      res.write(message);
    }, refreshRate);
  }

  // Client side
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end(`
    <!DOCTYPE html>
    <html lang="en" dir="ltr">
      <head>
        <meta charset="utf-8">
        <title>SSE</title>
      </head>
      <body>
        <pre id="log"></pre>
      </body>
      <script>
        var eventSource = new EventSource('/events');
        eventSource.onmessage = function(event) {
          document.getElementById('log').innerHTML += event.data + '<br>';
        };
      </script>
    </html>
  `);
});

server.listen(port);

server.on('error', (err) => {
  console.log(err);
  process.exit(1);
});

server.on('listening', () => {
  console.log(`Listening on port ${port}`);
});

Upvotes: 19

Yurii Miniv
Yurii Miniv

Reputation: 91

If you're using express this is the easiest way https://www.npmjs.com/package/express-sse

on BE:

const SSE = require('express-sse');

const sse = new SSE();

...

app.get('/sse', sse.init);

...

sse.send('message', 'event-name');

on FE:

const EventSource = require('eventsource');

const es = new EventSource('http://localhost:3000/sse');

es.addEventListener('event-name', function (message) {
  console.log('message:', message)
});

Upvotes: 9

Brent Washburne
Brent Washburne

Reputation: 13158

Here is an express server that sends one Server-Sent Event (SSE) per second, counting down from 10 to 0:

const express = require('express')

const app = express()
app.use(express.static('public'))

app.get('/countdown', function(req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  })
  countdown(res, 10)
})

function countdown(res, count) {
  res.write("data: " + count + "\n\n")
  if (count)
    setTimeout(() => countdown(res, count-1), 1000)
  else
    res.end()
}

app.listen(3000, () => console.log('SSE app listening on port 3000!'))

Put the above code into a file (index.js) and run it: node index

Next, put the following HTML into a file (public/index.html):

<html>
<head>
  <script>
  if (!!window.EventSource) {
    var source = new EventSource('/countdown')

    source.addEventListener('message', function(e) {
      document.getElementById('data').innerHTML = e.data
    }, false)

    source.addEventListener('open', function(e) {
      document.getElementById('state').innerHTML = "Connected"
    }, false)

    source.addEventListener('error', function(e) {
      const id_state = document.getElementById('state')
      if (e.eventPhase == EventSource.CLOSED)
        source.close()
      if (e.target.readyState == EventSource.CLOSED) {
        id_state.innerHTML = "Disconnected"
      }
      else if (e.target.readyState == EventSource.CONNECTING) {
        id_state.innerHTML = "Connecting..."
      }
    }, false)
  } else {
    console.log("Your browser doesn't support SSE")
  }
  </script>
</head>
<body>
  <h1>SSE: <span id="state"></span></h1>
  <h3>Data: <span id="data"></span></h3>
</body>
</html>

In your browser, open localhost:3000 and watch the SSE countdown.

Upvotes: 76

Manish Kumar
Manish Kumar

Reputation: 548

**client.js**

var eventSource = new EventSource("/route/events");
eventSource.addEventListner("ping", function(e){log(e.data)});

//if no events specified
eventSource.addEventListner("message", function(e){log(e.data)});

**server.js**

http.createServer((req, res)=>{

    if(req.url.indexOf("/route/events")>=){

      res.setHeader('Connection', 'keep-alive');

      res.setHeader("Cache-Control", "no-cache");

      res.setHeader("Content-Type", "text/event-stream");

      let event = "event: ping";

      let id = `id: ${Date.now()}`;

      let data = {
         message:`hello @${new Date().toString()}`
      }

      data = "data: "+JSON.stringify(data);

      res.end(`${event}\n${id}\n${data}\n\n`);
   }
}).listen(PORT)

Upvotes: 2

Gurucharan M K
Gurucharan M K

Reputation: 905

I found SSE implementation in node.js.

Github link: https://github.com/einaros/sse.js

NPM module:https://www.npmjs.com/package/sse

Will above link helps you ?

Upvotes: 3

user6335777
user6335777

Reputation:

You should be able to do such a thing using Socket.io. First, you will need to install it with npm install socket.io. From there, in your code you will want to have var io = require(socket.io);

You can see more in-depth examples given by Socket.IO

You could use something like this on the server:

var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 3000;

server.listen(port, function () {
  console.log('Server listening at port ' + port);
});

app.use(express.static(__dirname + '/public'));

io.on('connection', function (socket) {
    socket.emit('EVENT_NAME', {data});
});

And something like this on the client:

<script src="socket_src_file_path_here"></script>
<script>
  var socket = io('http://localhost');
  socket.on('EVENT_NAME', function (data) {
    console.log(data);
    //Do whatever you want with the data on the client
  });
</script>

Upvotes: -42

Related Questions