Author avatar

Zachary Bennett

Handling Nested HTTP Requests Using the Fetch API

Zachary Bennett

  • Aug 6, 2020
  • 5 Min read
  • 553 Views
  • Aug 6, 2020
  • 5 Min read
  • 553 Views
Web Development
Front End Web Development
Client-side Framework
React

Introduction

If you remember the days of having to use the XMLHttpRequest (XHR) construct to make HTTP requests in JavaScript, you can especially appreciate the more modern Fetch API. With XHR requests, JavaScript developers are limited to using callbacks in order to model asynchronicity. Fetch allows you to create HTTP requests, which are easily handled using JavaScript Promises. In terms of handling asynchronous control-flow, Promises are a massive step up from callbacks. This is especially true when you need to chain multiple requests, one after the other.

In this guide, you will learn how to effectively handle the chaining of fetch calls in order to avoid nesting them. First, you will take a look at the naive approach to modeling chained HTTP requests, which is using nested calls to fetch. This will be followed up with some optimizations that include Promise chaining and use of the async and await keywords.

Let's dive in!

Naive Approach

Your first instinct when using fetch might be to do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// my-component.jsx

// We have to get chips after we get fish...
getFishAndChips() {
    fetch(this.fishApiUrl) // Request fish species
        .then(fishRes => {
            fishRes.json().then(fish => {
                this.fish = fish;

                const fishIds = fish.map(fish => fish.id);

                fetch( // Request chips using fish ids
                        this.chipsApiUrl,
                        {
                            method: 'POST',
                            body: JSON.stringify({ fishIds })
                        }
                    )
                    .then(chipsRes => {
                        chipsRes.json().then(chips => {
                            this.chips = chips;
                        })
                    })
            })
        })
}
javascript

In the above code, we are nesting two fetch calls in order to ensure that we request our chips after we request our fish. This is because, in order to request chips, we need to send an array of fish IDs with the POST request. This works, however, there is a big readability problem here. It is possible to reduce the amount of code needed for this feature dramatically. Let's take a look at Promise chaining!

Fetch Promise Chaining

The first way to remove the nesting of these fetch calls is to use what is known as Promise chaining. Promise chaining can be achieved by simply returning another Promise from within a call to Promise.then. The following example shows how to do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// We have to get chips after we get fish...
getFishAndChips() {
    fetch(this.fishApiUrl) // Request fish species
        .then(response => response.json())
        .then(fish => {
            this.fish = fish;

            const fishIds = fish.map(fish => fish.id);

            return fetch( // Request chips using fish ids
                        this.chipsApiUrl,
                        {
                            method: 'POST',
                            body: JSON.stringify({ fishIds })
                        }
                    );
        })
        .then(response => response.json())
        .then(chips => {
            this.chips = chips;
        });
}
javascript

This is great! The code is much more maintainable, succinct, and readable. But we can do better! With the new async/await syntax that has been brought into JavaScript, we can remove the use of multiple calls to Promise.then entirely! You will see how this can be achieved in the next section.

A Better Way: Async/Await

The async/await keywords are a wonderful mechanism for modeling asynchronous control-flow in computer programs. In JavaScript, these keywords are syntactic sugar on top of Promises--they abstract away the calls to Promise.then. In the following code, we refactor the getFishAndChips function to use async/await.

1
2
3
4
5
6
7
8
9
10
11
// We have to get chips after we get fish...
async getFishAndChips() {
    const fish = await fetch(this.fishApiUrl).then(response => response.json());
    this.fish = fish;

    const fishIds = fish.map(fish => fish.id),
      chipReqOpts = { method: 'POST', body: JSON.stringify({ fishIds }) };

    const chips = await fetch(this.chipsApiUrl, chipReqOpts).then(response => response.json());
    this.chips = chips;
}
javascript

Wow! We have dramatically condensed the code used while achieving the same, desired effect. Well done! The async/await mechanism for control flow is an extremely powerful way to chain HTTP requests within your app.

Conclusion

In this guide, you have learned how to avoid making nested calls to the JavaScript Fetch API when chaining HTTP requests. You learned that a solution to nested fetch calls can be found in both Promise chaining and async/await. Undoubtedly, the most readable code was achieved via the use of async/await syntax.

You can now be confident in writing modern and readable asynchronous code within your React components!

3