Utilizing Observables in NestJS: Exploring SSE and EventEmitter

I am working on a project where I need to display an event that occurs in the backend on the frontend. Since it is a one-way communication, I have decided to use SSE (Server Sent Events) in nestjs to push the event to the frontend. The setup, as per the documentation, is quite straightforward:

@Sse('sse')
sse(): Observable<MessageEvent> {
  return interval(1000).pipe(map((_) => ({ data: { hello: 'world' } })));
}

Although this setup works, I now want to push the actual "event" happening in the backend instead of using interval. Here is my current setup:

@Injectable()
export class StocksService {
  public stocks: Stock[] = [
    {
      id: 1,
      symbol: 'Stock #1',
      bid: 500,
      ask: 500,
    }
  ];

  constructor(private eventEmitter: EventEmitter2) {}

  create(createStockDto: CreateStockDto) {
    const stock = {
      id: this.stocks.length + 1,
      ...createStockDto,
    };
    this.stocks.push(stock);

    const stockCreatedEvent = new StockCreatedEvent();
    stockCreatedEvent.symbol = stock.symbol;
    stockCreatedEvent.ask = stock.ask;
    stockCreatedEvent.bid = stock.bid;

    this.eventEmitter.emit('stock.created', stockCreatedEvent);

    return stock;
  }
}

The line

this.eventEmitter.emit('stock.created', stockCreatedEvent);
emits the event, and I can see it in the console using a listener:

@Injectable()
export class StockCreatedListener {
  @OnEvent('stock.created')
  handleStockCreatedEvent(event: StockCreatedEvent) {
    console.log(event);
  }
}

Now, I want to push this data to the frontend using SSE. I tried creating an Observable like this:

  @Sse('sse')
  @OnEvent('stock.created')
  sse(event: StockCreatedEvent): Observable<MessageEvent> {
    const obj = of(event);
    return obj.pipe(map((_) => ({ data: event})));
  }

However, when I visit the URL http://localhost:3000/sse, nothing happens. It doesn't log anything or return any stream. Do I need an observable here, or should I be using a subject?

I would appreciate any help or guidance on this. You can also check out the repository for more specific details.

Answer №1

I'm currently working on a similar project.

To notify connected clients only when an asset's price is updated, I opted for a webhook instead of short polling.

The solution I implemented is similar to the following (I have omitted some less relevant parts):

LiveQuoteController:

//... other imports
import { EventEmitter2 } from "@nestjs/event-emitter";
import { Observable, fromEvent } from "rxjs";
import { map } from "rxjs/operators";

@Controller("/livequote")
export class LiveQuoteController {
    constructor (private readonly eventEmitter: EventEmitter2) {}

    @Sse()
    public liveQuote(): Observable<MessageEvent> {
        return fromEvent(this.eventEmitter, "price.update")
            .pipe(map(price => {
                return { data: price } as MessageEvent;
            }));
    }
}

PriceUpdateHookController:

//... other imports
import { EventEmitter2 } from "@nestjs/event-emitter";

@Controller("/price")
export class PriceUpdateHookController {
    constructor (private readonly eventEmitter: EventEmitter2) { }

    @Post()
    updatePrice(@Body() newPrice: number) {
        this.eventEmitter.emit("price.update", newPrice);
        return true;
    }
}

I hope this information is useful.

Answer №2

I recently updated the URL http://localhost:3000/sse within React's App.tsx file on line 6.

It has been successful for me.

on the client side

Before

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

After

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

on the server side

The same message is displayed; I simply updated the current time on the server side. In app.controller.ts,

Before

  sse(event: StockCreatedEvent): Observable<MessageEvent> {
    return interval(1000).pipe(map((_) => ({ data: { hello: 'world' } })));
  }

After

  sse(event: StockCreatedEvent): Observable<MessageEvent> {
    return interval(1000).pipe(map((_) => ({ data: { hello: 'world, time = ' +  new Date().toUTCString() } })));
  }

Outcome

server side - backend (nestjs)

https://i.sstatic.net/bBCTP.png

client side - client (React)

https://i.sstatic.net/Vw235.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Display Image based on AngularJS value

Within my data, there exists a value {{catadata2.EndorsementList['0'].Rating}}. This value can be either 3, 4, or 5. Based on this value, I am looking to display the image <img src="/assets/img/rating.png" /> a certain number of times. For ...

Error: The variable __WEBPACK_EXTERNAL_MODULE_XX__ has not been defined

A unique npm package called our-map has been developed utilizing TypeScript, webpack, and the ArcGIS JS API to encapsulate an esri map within a React component. The functionality of the module has been verified through testing on a dedicated page within th ...

What is the best way to define a model class within my Angular 2 component using TypeScript?

As I delve into Angular 2 and TypeScript, I am keen on adopting best practices. I have decided to move away from a simple JavaScript model ({ }) in favor of creating a TypeScript class. However, it seems that Angular 2 is not very fond of my approach. T ...

Issue with handling promise rejection of type error in node.js

Whenever I try running node server.js in Node.js, an error message pops up saying 'Cannot read property 'length' of undefined'. Despite installing all the necessary libraries (such as request) and browsing through various related posts ...

Exploring the integration of d3 in an Express application - encountering an error: document is not recognized

I am facing a challenge in my expressjs application where I need to dynamically render vertices in a graph using d3. However, the code execution order seems to be causing issues for me. When attempting to use the d3.select function, I encounter the followi ...

JavaScript design not aligning

I'm currently attempting to find a pattern that includes the pipe (|) operator. Here is the code I've used to match the pattern: var format = /[ \\|]/; // This is the pattern for matching the pipe pattern if ("Near raghavendra temple ...

Enhance your coding experience with code completion and autocomplete in Angular/Typescript using ATOM within

Is it possible to have Codecompletion / Autocomplete in Atom similar to Webstorm? Currently I am getting familiar with TypeScript and really enjoying it, but the lack of Codecompletion support for my HTML files in Atom is quite frustrating. Having this f ...

Analyzing the list of paths that are passed to the function

I am looking for assistance in creating an asynchronous "getTypes" function that can analyze a list of paths and return an array describing the type of content in each path. The function should handle all cases efficiently and in case of any errors during ...

Guide on utilizing Three.js OrbitControl with several objects

I managed to get the orbit control feature working, but I am facing an issue where controlling one object also ends up controlling all three objects on the page. Additionally, pan/zoom functionality does not seem to work at all with the OrthographicCamera. ...

Dynamic class/interface in Typescript (using Angular)

Is there a way to achieve intellisense for an object created with a dynamic class by passing parameters? Here is the code snippet: Main: let ita: any = new DynamicClass('ITA'); let deu: any = new DynamicClass('DEU'); The DynamicClass ...

Angular and JavaScript Performing Slide-Up Animation

Currently, I am working on creating a menu using Angular and JavaScript. My goal is to have this menu toggle between showing and hiding when a button is clicked. If you would like to view the code that I have written so far, you can check out the demo her ...

Adjust the colors of the borders upon clicking the button

Hello, I'm attempting to create a function where clicking on one button changes its border color to blue and changes the border color of all other buttons to orange. However, I'm encountering an issue where the border color for the other buttons ...

How can I position two divs side by side within an Appbar?

I would like the entire Container to be in a single row, with the Typography centered as it already is, and the toggle-container to float to the right <AppBar className={styles.AppBar}> <Toolbar> <Container> ...

What is the best way to determine the range in which the value falls?

Currently, I am working on validating whether a User has the required karma (reputation) to perform certain actions, such as placing a bid on an item. The karma value falls within the interval [-25; 100]. Additionally, it is noted that as a user accumulate ...

Angular - ui-router states are not being detected

I'm currently working on a Spring and Angular JS web application project. The structure of the project is as follows:https://i.sstatic.net/xgB4o.png app.state.js (function() { 'use strict'; angular .module('ftnApp') .con ...

What steps can be taken to troubleshoot a TypeScript-powered Node.js application running in WebStorm?

Seeking advice on debugging a node.js application utilizing TypeScript within WebStorm - any tips? ...

When integrating string variables into JavaScript regular expressions in Qualtrics, they seem to mysteriously vanish

I have been working on a project to analyze survey responses in Qualtrics by counting the number of matches to specific regular expressions. For example, whenever phrases like "I think...", "In my opinion," are used, the count increases by one. Below is t ...

Removing a specific object from an array in Node.js

Here is the structure of my database.json file: { "opened_tickets": [ { "userid": "4", "ticketid": "customer_info", "opened_timestamp": "1662543404514" }, ...

Is there a way to determine the bounding rectangle of a specific word within a paragraph when you only have its index?

I am currently developing a text-to-speech functionality for a react application that can emphasize the word being spoken by highlighting it with a background color. This feature closely resembles the layout of the Firefox reader view. However, my curren ...

Dealing with Error TS2769 in Visual Studio Code when passing props to a custom component in Vue 2 with Typescript

I've encountered an issue with a Vue JS component that involves passing a custom prop. I am utilizing the Vue Options API without utilizing the class component syntax. Whenever I pass ANY prop to my custom component the-header, I receive an error sta ...