How can I chain HTTP calls in Angular 2?

HttpAngular

Http Problem Overview


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?

Http Solutions


Solution 1 - Http

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 - Http

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
}

Solution 3 - Http

You could also chain Promises. Per this example

<html>
<head>
  <meta charset="UTF-8">
  <title>Chaining Promises</title>
</head>

<body>

  <script>
    const posts = [
      { title: 'I love JavaScript', author: 'Wes Bos', id: 1 },
      { title: 'CSS!', author: 'Chris Coyier', id: 2 },
      { title: 'Dev tools tricks', author: 'Addy Osmani', id: 3 },
    ];

    const authors = [
      { name: 'Wes Bos', twitter: '@wesbos', bio: 'Canadian Developer' },
      { name: 'Chris Coyier', twitter: '@chriscoyier', bio: 'CSS Tricks and Codepen' },
      { name: 'Addy Osmani', twitter: '@addyosmani', bio: 'Googler'},
    ];

    function getPostById(id) {
      // Create a new promise
      return new Promise((resolve, reject) => {
         // Using a settimeout to mimic a database/HTTP request
         setTimeout(() => {
           // Find the post we want
           const post = posts.find(post => post.id == id);
           if (post) {
              resolve(post) // Send the post back
           } else {
              reject(Error('No Post Was Found!'));
           }
         },200);
      });
    }

    function hydrateAuthor(post) {
       // Create a new promise
       return new Promise((resolve, reject) => {
         // Using a settimeout to mimic a database/http request
         setTimeout(() => {
           // Find the author
           const authorDetails = authors.find(person => person.name === post.author);
           if (authorDetails) {
             // "Hydrate" the post object with the author object
             post.author = authorDetails;
             resolve(post);
           }
           else {
  	          reject(Error('Can not find the author'));
           }
         },200);
       });
    }

    getPostById(4)
      .then(post => {
         return hydrateAuthor(post);
      })
      .then(post => {
         console.log(post);
      })
      .catch(err => {
         console.error(err);
      });
  </script>

</body>
</html>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzlajaView Question on Stackoverflow
Solution 1 - HttpTGHView Answer on Stackoverflow
Solution 2 - Httpxrobert35View Answer on Stackoverflow
Solution 3 - HttpJGFMKView Answer on Stackoverflow