CAP
CAP

Reputation: 140

NestJS API calls and reading response data within the API

I'm building a NestJS api and need to make a call to an outside API and parse the responding data. This is a healthCheck to see if another API is up and running. Here is the basic call:

    @Get('healthCheck')
    public async healthCheck(
        @Req() req: Request,
        @Res() res: Response,
    )
    {
        const obj = this.test(req);
        console.log(obj)
    }

    test(req) {
        const testingURL = 'https://google.com';
        return this.http.get(testingURL).pipe(
            map((obj: AxiosResponse) => { return obj.data } ),
            tap(console.log)
        );
    }

I've worked extensively with angular in the past and doing any sort of ".toPromise()" or "subscribe()" on the returned data causes a "Converting circular structure to JSON".

Currently the "console.log(obj)" in the first section is printing out an observable object that cannot be parsed:

Observable {
  source: Observable {
    source: Observable { _subscribe: [Function (anonymous)] },
    operator: [Function (anonymous)]
  },
  operator: [Function (anonymous)]
}

Any advice or helpful hints would be helpful. Most other posts that discuss this issue say that simple mapping the response or adding a promise fixes the issue, but it does not explain further on how to parse that observable once it's been fetched.

Edit: solution posted below.

Upvotes: 2

Views: 13622

Answers (3)

Gustavo Contreiras
Gustavo Contreiras

Reputation: 570

You should inject HttpService from HttpModule (@nestjs/axios) and get the axios reference like this this.httpService.axiosRef.get(...)

See official documentation for more details.

Upvotes: 1

CAP
CAP

Reputation: 140

Thanks to https://stackoverflow.com/users/9576186/jay-mcdoniel for help on this and this project reference https://github.com/jmcdo29/heart-watch/blob/feat/nest-commander/src/character-reader/character-reader.api.service.ts

Here was my working solution:

  @Get('healthCheck')
  public async healthCheck() {
    const obj = await this.getEndpoint('https://google.com');
    console.log(obj);
  }

  private getEndpoint(url): Promise<any> {
    return lastValueFrom(
      this.http.get<any>(url).pipe(
        map((res) => {
          return res.data;
        }),
      ),
    );
  }

This should parse any get (or post) endpoint, as long as parameters/auth isn't required, and return the observable parsed into a promise in nestjs using axios.

Edit: as Jay suggested, lastValueFrom used instead of depricated toPromise()

Upvotes: 2

Jay McDoniel
Jay McDoniel

Reputation: 70061

  1. unless you have a reason, don't inject @Res() to the route handler, just return your data and let Nest handle it rather than having to call res.send(). If you need access to response headers or setting cookies, use @Res({ passthrough: true })

  2. You can return an observable in Nest directly and Nest will handle reading the data and sending it back for you.

  3. if you need to read a value inside the observable, you can use the tap operator from RxJS and pipe(tap(console.log)) to read the data


EDIT 10/15/2021

Now that I've also read that you're wanting to use this data in another API call, you have two options:

  1. use lastValueFrom to convert the RxJS Observable into a Promise so that it can be awaited. Straightforward and easy to work with

  2. use operators like mergeMap or switchMap and chain together your Observables, still returning a single observable in the end. RxJS is like really powerful callbacks with extra options on top, but they can also get complex so this option usually requires a bit more nuance and dedication to ensure the operators are chained correctly.

Upvotes: 0

Related Questions