Multiple sequential API calls using RXJS in Angular 12 app

Viewed 1925

I have a requirement where, once I get a response from my first API call, I need to make 2 API calls (can be in parallel). Once all the API call's response is got, I need to send the observables (obs2$ & obs3$) to the subcomponent.

Code is something like:

ngOnInit(): void {
    this.obs1$ = this.getData1();
    this.obs2$ = this.obs1$.pipe(
        concatMap(() => {
            return this.getData2();
        })
    );
    this.obs3$ = this.obs1$.pipe(
        concatMap(() => {
            return this.getData3();
        })
    );
}

private getData1(): Observable<string> {
    return this.service.getData1<string>('/api/get_1').pipe(
        tap((response: string) => {
            this.id = response
        })
    );
}

private getData2(): Observable<string> {
    return this.service.getData2<string>('/api/get_2/id='+this.id).pipe(
        tap((response: string) => {
            ...
            this.flag2 = true;
        })
    );
}

private getData3(): Observable<string> {
    return this.service.getData3<string>('/api/get_3/id='+this.id).pipe(
        tap((response: string) => {
            ...
            this.flag3 = true;
        })
    );
}

My .html is like this:

<ng-container *ngIf="(data2: obs2$ | async, data3: obs3$ | async) as data; else loading">
    <ng-container *ngIf="flag2 && flag3; else loading">
        <sub-component
            [data2]="data.data2"
            [data3]="data.data3"
        >
        <sub-component>
    </ng-container>
</ng-container>
<ng-template #loading> Loading... </ng-template>

The issue is API call of getData1 is getting called 2 times. Is there a way to have it in single. Also, if I can do it without using the flags (flag 2 & flag3). Note: I tried this, but it did not work:

ngOnInit(): void {
    this.obs1$ = this.getData1();
    this.obs1$.pipe(first()).subscribe(() => {
        this.obs2$ = this.getData2();
        this.obs3$ = this.getData3();
    });
}
2 Answers

You're almost there. Instead of triggering individual calls for data2 and data3, you could use the RxJS forkJoin function to trigger multiple observables in parallel.

You could also skip the variables id, flag2 and flag3 this way.

Try the following

ngOnInit(): void {
  this.obs$ = this.getData1().pipe(
    switchMap((id: string) => forkJoin({
      data2: this.getData2(id),
      data3: this.getData3(id)
    }))
  );
}

private getData1(): Observable<string> {
  return this.service.getData1<string>('/api/get_1');
}

private getData2(id: string): Observable<string> {
  return this.service.getData2<string>('/api/get_2/id=' + id);
}

private getData3(id: string): Observable<string> {
  return this.service.getData3<string>('/api/get_3/id=' + id);
}
<ng-container *ngIf="(obs$ | async) as data; else loading">
  <sub-component 
    [data2]="data.data2" 
    [data3]="data.data3"
  >
  <sub-component>
</ng-container>
<ng-template #loading> Loading... </ng-template>

If you need the obs2$ & obs3$ to be called on parallel with one HTTP request for obs1$, you can achieve that by adding the shareReplay operator to the obs1$.

Try it like the following:

ngOnInit(): void {
  this.obs1$ = this.getData1().pipe(shareReplay(1));
  this.obs2$ = this.obs1$.pipe(switchMap((id) => this.getData2(id)));
  this.obs3$ = this.obs1$.pipe(switchMap((id) => this.getData3(id)));
}

private getData1(): Observable<string> {
  return this.service.getData1<string>('/api/get_1');
}

private getData2(id: string): Observable<string> {
  return this.service.getData2<string>('/api/get_2/id=' + id);
}

private getData3(id: string): Observable<string> {
  return this.service.getData3<string>('/api/get_3/id=' + id);
}

Then regarding the flags, you can try the following within the template:

<ng-container *ngIf="{ data2: obs2$ | async, data3: obs3$ | async } as data; else loading">
  <ng-container *ngIf="data.data2 && data.data3; else loading">
    <sub-component [data2]="data.data2" [data3]="data.data3"></sub-component>
  </ng-container>
</ng-container>
<ng-template #loading> Loading... </ng-template>
Related