Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server Sent Events not receiving messages from stream with React and NodeJs

I have a very simple set up.
NodeJs

export default class StreamController {
    public static newMessage = async (req: Request, res: Response) => {
        const { userid } = req.params;
        res.writeHead(200, {
            "Connection": "keep-alive",
            "Content-Type": "text/event-stream",
            "Cache-Control": "no-cache",
        });
        setInterval(async () => {
            console.log(await MessagesController.hasNewMessage(userid));
            res.write(`${JSON.stringify({ hasUnread: await MessagesController.hasNewMessage(userid) })}`);
            res.write("\n\n");
        }, 5000);
    }
}

React

 constructor() {
        super();
        const uid = getUserId();
        const eventSource = new EventSource(`http://localhost:8080/stream/messages/${uid}`)

        eventSource.onmessage = (e) => {
            console.log(e)
        }
    }

I can see that stream was opened but none event was passed from the server, while data was issued on the server side. What I'm doing wrong?

like image 745
andrey.shedko Avatar asked Jul 28 '19 12:07

andrey.shedko


1 Answers

You are passing the data on server side, but are not passing the event name as event. This is how I solved the issue (simplified reproduction):

Server:

app.get('/stream', (req, res) => {
  console.log('request received');
  res.writeHead(200, {
    "Connection": "keep-alive",
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
  });
  setInterval(async () => {
    res.write('event: ping\n');  // added these
    res.write(`data: ${JSON.stringify({ hasUnread: true })}`);
    res.write("\n\n");
  }, 5000);
});

Client:

constructor(props) {
  super(props);
  const eventSource = new EventSource(`http://localhost:3030/stream`);
  eventSource.onopen = e => {
    console.log(e);
  }
  eventSource.onmessage = e => {
    console.log('onmessage');
    console.log(e);
  }
  eventSource.addEventListener('ping', e => {
    console.log(e);
  });
}

The ping event will be firing every 5 seconds.

Now, if you want the eventSource.onmessage to be called, you should name the event as message on server side:

...
setInterval(async () => {
  res.write('event: message\n');  // message event
  res.write(`data: ${JSON.stringify({ hasUnread: true })}`);
  res.write("\n\n");
}, 5000);
...

Now you will see in the console:

onmessage MessageEvent {..., data: "{'hasUnread': true}", ...}

Note that in this case the ping event won't be fired any more. More info here.

like image 125
Anton Bahurinsky Avatar answered Oct 18 '22 12:10

Anton Bahurinsky