Server side events using nodeJS
Tags: node, typescript
Published: 2021-12-12 15:27:37

Server-Sent Events (SSE) is a technology based on HTTP. On the client-side, it provides an API called EventSource (part of the HTML5 standard) that allows us to connect to the server and receive updates from it.

Let's create a simple chat system

First, we are gonna create a new nodeJS project

Let's name it **sse-server**

Now, let's initiate npm to install some dependencies


![Initiate NPM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7e9b1bzu4k3twc155klt.png)

We are gonna add `express`, `body-parser` and `cors`


![NPM Install command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9o6tabfduz2o6y2ynv4k.png)

In **index.ts**

* Configure the middleware
```
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';

const app = express();

app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
```

* Specify the PORT
```
const PORT = 3000;
```

* Declare and initialise the required variables
```
// Contains the connected clients info
let clients: any[] = []; 
// Contains the messages of the network
const messages: any[] = [];
```

* Create a endpoint where the clients will subscribe to events
```
app.get('/messages', (request: any, response: any) => {
  const headers: any = {
    'Content-Type': 'text/event-stream',
    Connection: 'keep-alive',
    'Cache-Control': 'no-cache'
  };

  response.writeHead(200, headers);

  const data: string = `data: ${JSON.stringify(messages)}\n\n`;

  response.write(data);

  const clientId = (request.query.id ?? 'guest') + '_' + Date.now();

  const newClient: any = {
    id: clientId,
    response
  };

  clients.push(newClient);

  request.on('close', () => {
    console.log(`${clientId} Connection closed`);
    clients = clients.filter((client: any) => client.id !== clientId);
  });
});
```

* A function to notify whenever a new message arrives in the network
```
const notifyMessageToAllUsers = (userIdWithoutUnderscore: string) => {
  clients
    .filter(
      (client: any) =>
        String(client.id).split('_')[0] != userIdWithoutUnderscore
    )
    .forEach((client: any) => {
      client.response.write(`data: ${JSON.stringify(messages)}\n\n`);
    });
};
```

* Finally an endpoint to push messages to the network
```
app.post('/message', (request, response) => {
  if (
    request.query.id &&
    request.body &&
    request.body.message &&
    String(request.body.message).trim()
  ) {
    messages.push({
      user_id: request.query.id,
      message: request.body.message.trim()
    });
    notifyMessageToAllUsers(String(request.query.id));
    response.send({ status: 200, error: 'Message Sent Succesfully' });
  } else {
    response.send({ status: 400, error: 'Bad Request' });
  }
});
```

* let's start the server
```
app.listen(PORT, () => {
  console.log(`Server is listening at http://localhost:${PORT}`);
});
```

```
$ npm start
```

Server console
```
$ Server is listening at http://localhost:3000
```

For pushing a message to the network
```
$ curl --location --request POST 'http://localhost:3000/message?id=1234' \
--header 'Content-Type: application/json' \
--data-raw '{
    "message": "Hi"
}'
```

To listen to the messages
```
curl --location --request GET 'http://localhost:3000/messages' \
--header 'Accept: text/event-stream'
```

After subscribing to the endpoint, we should be able to see the message that was pushed earlier 
```
$ data: [{"user_id":"1234","message":"Hi"}]
```

## Conclusion

SSE is very powerful in terms of receiving **events** from the server to achieve real-time behaviour in the client. But it can get very expensive if it's not implemented efficiently.

## Thanks for Reading!

Thanks for reading, I hope you enjoyed it!