How can I chain HTTP calls in Angular 2?

I'm new to Angular 2 and HTTP Observables. I have a component which calls an HTTP service and returns an Observable. Then I subscribe to that Observable and it works fine.

Now, I want, in that component, after calling the first HTTP service, if the call was successful, to call another HTTP service and return that Observable. So, if the first call is not successful the component returns that Observable, opposite it returns Observable of the second call.

What is the best way to chain HTTP calls? Is there an elegant way, for example like monads?


Solution 1:

You can do this using the mergeMap operator.

Angular 4.3+ (using HttpClientModule) and RxJS 6+

import { mergeMap } from 'rxjs/operators';

this.http.get('./customer.json').pipe(
  mergeMap(customer => this.http.get(customer.contractUrl))
).subscribe(res => this.contract = res);

Angular < 4.3 (using HttpModule) and RxJS < 5.5

Import the operators map and mergeMap, then you can chain two calls as follows:

import 'rxjs/add/operator/map'; 
import 'rxjs/add/operator/mergeMap';

this.http.get('./customer.json')
  .map((res: Response) => res.json())
  .mergeMap(customer => this.http.get(customer.contractUrl))
  .map((res: Response) => res.json())
  .subscribe(res => this.contract = res);

Some more details here: http://www.syntaxsuccess.com/viewarticle/angular-2.0-and-http

More information about the mergeMap operator can be found here

Solution 2:

Using rxjs to do the job is a pretty good solution. Is it easy to read? I don't know.

An alternative way to do this and more readable (in my opinion) is to use await/async.

Example:

async getContrat(){
    // Get the customer
    const customer = await this.http.get('./customer.json').toPromise();

    // Get the contract from the URL
    const contract = await this.http.get(customer.contractUrl).toPromise();

    return contract; // You can return what you want here
}

Then call it :)

this.myService.getContrat().then( (contract) => {
  // do what you want
});

Or in an async function:

const contract = await this.myService.getContrat();

You can also use try/catch to manage the error:

let customer;
try {
  customer = await this.http.get('./customer.json').toPromise();
}catch(err){
   console.log('Something went wrong will trying to get customer');
   throw err; // Propagate the error
   //customer = {};  // It's a possible case
}