Sling Academy
Home/Node.js/How to Push Server-Sent Events (SSE) in NestJS

How to Push Server-Sent Events (SSE) in NestJS

Last updated: January 01, 2024

Introduction

Server-Sent Events (SSE) provide a unidirectional client-server communication channel where the server can send updates to the client. This tutorial will guide you through pushing SSE in NestJS, leveraging the latest features of TypeScript.

Getting Started with SSE in NestJS

Server-Sent Events allow servers to push information to clients that have subscribed to these events once a connection has been made. Unlike WebSocket, SSE establishes a one-way channel from the server to the client, which is perfect for chat applications, live updates, or any scenario where you need to stream data from the server.

Prerequisites

  • Node.js installed
  • NestJS CLI installed
  • Basic understanding of TypeScript and NestJS

Creating a New NestJS Project

Firstly, let’s create a new NestJS project:

nest new sse-app

Creating a Basic SSE Controller

We’ll start by creating an SSE controller that can send events to the client. Here is a simple example:

import { Controller, Sse } from '@nestjs/common';
import { Observable, interval } from 'rxjs';
import { map } from 'rxjs/operators';

@Controller('events')
export class EventsController {
  @Sse('sse-stream')
  sseStream(): Observable<any> {
    return interval(1000).pipe(
      map((_) => {
        return { data: 'ping' };
      })
    );
  }
}

This example sets up a basic SSE endpoint at /events/sse-stream that sends a ‘ping’ to the client every second.

Advanced Usage

Going further, you might want to send more complex data, handle connection issues, or deal with multi-user scenarios.

Multi-User SSE Streams

Let’s explore creating a system that personalizes the SSE stream for different users:

import { Injectable, Sse } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class EventService {
  private userEvents = new Map<string, Observable<any>>();

  register(userId: string, events$: Observable<any>) {
    this.userEvents.set(userId, events$);
  }

  @Sse('sse-stream/:userId')
  getUserStream({ params }): Observable<any> {
    const userId = params.userId;
    return this.userEvents.get(userId) || new Observable();
  }
}

Here we have a service to hold different streams for different users and a controller method to return the specific user’s stream.

Handling Connection Issues and Retries

Connections over SSE could drop due to various reasons, and it’s essential to handle these gracefully:

import { Injectable } from '@nestjs/common';
import { EMPTY } from 'rxjs';

revivingStream(userId: string) {
  return this.userEvents.get(userId) || EMPTY;
}

The EMPTY constant ensures that if there’s no existing stream for the user, it doesn’t throw an error but instead sends an empty stream which is valid for SSE.

Dynamic Event Names

NestJS also gives you the flexibility to define dynamic event names using the @Sse decorator:

// ... other imports
import { MessageEvent } from 'http';

// ... events code

@Sse('sse-stream')
sseDynamicNames(): Observable<MessageEvent> {
  return eventGenerator$.pipe(
    map((value) => ({ type: 'customEventName', data: value }))
  );
}

Integrating with Frontend

To consume SSE on the frontend, you can use the EventSource interface available in modern browsers:

const eventSource = new EventSource('/events/sse-stream');
eventSource.onmessage = function(event) {
  console.log('New message:', event.data);
};
eventSource.addEventListener('customEventName', function(event) {
  console.log('Custom event:', event.data);
});

Conclusion

In this tutorial, we’ve explored the setup and implementation of SSE with NestJS. By using the Observable stream’s power, along with specific decorators and services, you can efficiently stream data to your clients in real-time. Through careful design, you can make use of SSE’s simplicity and reliability for your real-time applications.

Next Article: How to Hash Passwords in NestJS

Previous Article: How to run a queue of tasks in NestJS

Series: Nest.js Tutorials: From Basics to Advanced

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates