Have you ever wanted to send real-time updates to your clients without the need for a WebSocket connection? If so, then Server-Sent Events (SSE) is the perfect solution for you. In this blog, I'll show you how to implement this solution in a NestJS application.
Unlike WebSockets, SSE is a unidirectional communication protocol. This means that the server can only send data to the client, but the client cannot send data back to the server. This is a great solution for use cases where the client only needs to receive some data from the server, but not send any data back.
SSE is a part of the HTML5 specification and is supported by all modern browsers. It is also supported by Node.js, which makes it a great solution for building real-time applications with NestJS.
Before diving into the implementation, let's set up a new NestJS project and install the required dependencies.
Ensure you have the following tools installed on your system:
To create a new NestJS project, first, install the Nest CLI globally on your system by running:
npm install -g @nestjs/cli
Next, use the `nest new` command to create a new project. Replace `nestjs-sse-example` with your desired project name:
nest new nestjs-sse-example
Follow the prompts, and choose the package manager you prefer (npm or Yarn). The CLI will generate a new NestJS project with the default structure and configuration.
Navigate to the project directory and install the required dependencies:
cd nestjs-sse-example npm i npm i --save @nestjs/event-emitter # for the event emitter
The default NestJS installation already includes the necessary dependencies, such as `@nestjs/core`, `@nestjs/common`,
and `rxjs`. No additional dependencies are required for this example.
To start the development server, run the following command:
npm run start:dev
This will start the NestJS development server with hot-reload enabled. You should see the following output indicating that the server is running:
[nestjs] Starting Nest application... [nestjs] Nest application successfully started
Now that the NestJS server is set up and running accessible at http://127.0.0.1:3000, we can proceed to create an SSE endpoint in `app.controller.ts` made by the Nest CLI as
a boilerplate.
The way I'm going to implement this example is by creating an SSE endpoint that pipes the triggered event of `sse.event` to the response stream using
`fromEvent` from `rxjs`. This way, we can trigger the event from anywhere in the application and the client will receive the data.
import { Controller, Sse, MessageEvent } from '@nestjs/common'; import { AppService } from './app.service'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { Observable, fromEvent, map } from 'rxjs'; @Controller() export class AppController { constructor( private readonly appService: AppService, private eventEmitter: EventEmitter2, ) {} @Sse('/sse') async sse(): Promise<Observable<MessageEvent>> { return fromEvent(this.eventEmitter, 'sse.event').pipe( map((payload) => ({ data: JSON.stringify(payload), })), ); } }
The `@Sse` decorator is used to create an SSE endpoint. The `sse` method returns an `Observable` from `rxjs` that emits the `MessageEvent` object to the client.
The `MessageEvent` object contains the `data` property that is sent to the client. In this example, we are sending the `payload` object emitted by the
`sse.event` event.
The `MessageEvent` is imported from `@nestjs/common` and is not the same as the `MessageEvent` from the DOM API.
Now, let's go to `app.service.ts` and create a method that will trigger the `sse.event` event:
import { Injectable, OnModuleInit } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; @Injectable() export class AppService implements OnModuleInit { constructor(private eventEmitter: EventEmitter2) {} onModuleInit() { setInterval(async () => { this.sse(); }, 3000); } async sse() { this.eventEmitter.emit('sse.event', { message: 'Hello from the server', randomNumber: Math.random(), }); } }
The `onModuleInit` method is called when the module is initialized. In this example, we are using it to trigger the `sse` method every 3 seconds.
We're going to use a minimal Express.js that hosts static files to test the SSE endpoint.
First, create a new directory called `client` and navigate to it and install express:
mkdir client cd client npm i express
Next, create a new file called `app.js` and add the following code:
const express = require('express'); const path = require('path'); const app = express(); // Serve static files from the public directory app.use(express.static(path.join(__dirname, 'public'))); const port = 8888; app.listen(port, () => { console.log('Server accessible on http://127.0.0.1:' + port); });
This will start the Express server accessible at http://127.0.0.1:8888 and serve the static files from the `public` directory.
Next, create a new directory called `public` and create a new file called `index.html` in it. Add the following code to the file:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SSE Client</title> </head> <body> <h1>Server-Sent Events (SSE) Client</h1> <div id="events"> <h2>Events:</h2> <ul id="event-list"> </ul> </div> <script> document.addEventListener('DOMContentLoaded', () => { const eventList = document.getElementById('event-list'); const source = new EventSource('http://localhost:3000/sse'); source.onmessage = function (event) { const data = JSON.parse(event.data); const li = document.createElement('li'); li.textContent = JSON.stringify(data); eventList.appendChild(li); }; source.onerror = function (error) { console.error('EventSource error:', error); }; }); </script> </body> </html>
This will create a simple HTML page that will connect to the SSE endpoint and display the received events in a list.
The `EventSource` interface is used to connect to the SSE endpoint. The `onmessage` event is triggered when the client receives a new event from the server.
You can read more about the `EventSource` interface here.
To run the client, navigate to the `client` directory and run the following command:
node app.js
This will start the Express server and serve the static files from the `public` directory. You should see the following output in the terminal:
Server accessible on http://127.0.0.1:8888
Now, open http://127.0.0.1:8888 in your browser and you should see the following page:
Congratulations! You have successfully created an SSE endpoint in your NestJS application. You can now trigger the
`sse.event` event from anywhere in the application and the client will receive the data.