3

I'm usually a PHP developer with almost no experience with javascript/typescript. Just started using NestJS - I want to have an endpoint that returns a list of requisitions that was retrieved from another system via its endpoint.

The rough idea:

  1. Call the login endpoint with credentials
  2. Call the requisition listing endpoint with the token from the login endpoint
  3. Return the result

I have experimented around with calling an endpoint like so:

const url ='https://the-requisition-endpoint';
const config: AxiosRequestConfig = {
      headers: {
        'Accept': 'application/json',
        'Authorization': 'a_hard_coded_token_string' // hard coded for experimentation
      }
    };
let result = this.httpService.get(url, config);
result.subscribe(response =>
  console.log(response.data);
});

Now I need to change it so that the token is not hardcoded - to use the token from the login endpoint.

I'm not sure how to force the requisition endpoint to only be called once the login endpoint returns the token. I've searched and found that either a Promise.all() or a function called zip might help, but I don't know how to make it work.

Edwin
  • 886
  • 2
  • 13
  • 32

1 Answers1

5

I believe the RxJS operator you are looking for is the switchMap. Essentially, you'll want to end up doing something like this:

@Injcetable()
export class MyService {

  constructor(private readonly httpService: HttpService) {}

  getRequisitions(): Observable<any> {
    // change to POST signature is login is a post. post(url, body, config)
    return this.httpService.get(url, config).pipe(
      // optional, but it makes working with responses easier
      map(resp => resp.data),
      // make new http call and switch to this observable
      switchMap(loginData => this.httpService.get(url, configWithLoginData)),
      / again, optional, but useful for keeping data easily readable
      map(resp => resp.data),
      // optional, but useful for checking what is returned
      tap(data => console.log(data))
  }
}

All right, there's a lot going on here so let's break it down as well. In NestJS the built-in HttpService returns Observables instead of Promises. This allows you to do some really powerful stuff like retrying HTTP calls when certain failures arise. That can be done with promises, but it does take a bit more code. Anyways, the .pipe after the first HTTP call allows us to start manipulating the RxJS stream and work with the data as it comes through.

map is an RxJS operator, simialr to the Array.prototype.map operator, in that it applies a transformation function to each value the observable emits (in this case it is only one value, but it could work for many in theory).

switchMap is one of the flattening operators in RxJS, along with mergeMap and concatMap. I'll let you read up on those from the same link as above, but essentially, switchMap cancels the current observable in favor of the newly created one, which helps us to not have memory leaks. We return a new Observable (a new HttpService call), and start working with that data.

tap is a spying operator, as it doesn't do anything to the data it has access to, it just allows you to look at it (via console.log). This is pretty much the use for it, but you could make other callouts if needed and if the responses don't matter.

Lastly, there's no subscribe because NestJS will subscribe under the hood and send the last emitted value as the response. No need to worry about it ourselves.

Oh, and the response for all the map(resp => resp.data) is that an AxiosResponse has several fields to it, but for the most part, you probably only care about the return data held in the data attribute.

Each RxJS operator takes in a function, we're jsut using one line functions in each case here. You can use the full anonymous function syntax if you want though. i.e.

map((resp) => {
  return resp.data;
})

the one line syntax is just shorter, though it will require re-writing if additional logic that doesn't fit on one line must be performed.

Jay McDoniel
  • 57,339
  • 7
  • 135
  • 147
  • Hi, thanks for the response and the link, it was very helpful. One question though, how do I "execute" the whole thing? I have something like this: `let requisitionData = this.login().pipe( ...`, then when I do `console.log(requisitionData);` it says that it's an Observable. – Edwin Dec 27 '19 at 02:02
  • If this is for an HTTP response, i.e. some service called by a controller, you can just return the Observable and let NestJS subscribe under the hood. If this is manual execution, you can add `.subscribe({ next: (val) => void, error: (err) => void, complete: () => void})` (those are the signatures, you can play around with them as needed, just make sure they return void) and see what values are coming back in the `next` function. You can see some [more rxjs sample](https://github.com/jmcdo29/zeldaplay/tree/master/apps/api/src/app) in my server here. Also check out the tests to see subscribe – Jay McDoniel Dec 27 '19 at 04:00