using a fetch inside another fetch in javascript

JavascriptPromiseEs6 PromiseFetch Api

Javascript Problem Overview


I want to get an api and after that call another one. Is it wisely using a code like this in javascript?

fetch(url, {
 method: 'get',
 }).then(function(response) {  
  response.json().then(function(data) {  
    fetch(anotherUrl).then(function(response) {
      return response.json();
    }).catch(function() {
      console.log("Booo");
    });
  });  
}) 
.catch(function(error) {  
  console.log('Request failed', error)  
});

Javascript Solutions


Solution 1 - Javascript

Fetch returns a promise, and you can chain multiple promises, and use the result of the 1st request in the 2nd request, and so on.

This example uses the SpaceX API to get the info of the latest launch, find the rocket's id, and fetch the rocket's info.

const url = 'https://api.spacexdata.com/v4';

const result = fetch(`${url}/launches/latest`, { method: 'get' })
  .then(response => response.json()) // pass the data as promise to next then block
  .then(data => {
    const rocketId = data.rocket;

    console.log(rocketId, '\n');
  
    return fetch(`${url}/rockets/${rocketId}`); // make a 2nd request and return a promise
  })
  .then(response => response.json())
  .catch(err => {
    console.error('Request failed', err)
  })

// I'm using the result const to show that you can continue to extend the chain from the returned promise
result.then(r => {
  console.log(r.first_stage); // 2nd request result first_stage property
});

.as-console-wrapper { max-height: 100% !important; top: 0; }

Solution 2 - Javascript

There is not an issue with nesting fetch() calls. It depends on what you are trying to achieve by nesting the calls.

You can alternatively use .then() to chain the calls. See also https://stackoverflow.com/questions/40232165/how-to-structure-nested-promises/40232361#40232361

fetch(url)
.then(function(response) { 
  return response.json()
})
.then(function(data) {   
  // do stuff with `data`, call second `fetch`
  return fetch(data.anotherUrl)
})
.then(function(response) { 
  return response.json(); 
})
.then(function(data) {
  // do stuff with `data`
})
.catch(function(error) { 
  console.log('Requestfailed', error) 
});

Solution 3 - Javascript

This is a common question people get tripped up by when starting with Promises, myself included when I began. However, first...

It's great you're trying to use the new Fetch API, but if I were you I would use a XMLHttpRequest implementation for now, like jQuery AJAX or Backbone's overridden implementation of jQuery's .ajax(), if you're already using these libraries. The reason is because the Fetch API is still so new, and therefore experimental at this stage.

With that said, people definitely do use it, but I won't in my own production code until it's out of "experimental" status.

If you decide to continue using fetch, there is a polyfill available. NOTE: you have to jump through extra hoops to get error handling to work properly, and to receive cookies from the server. If you're already loading jQuery, or using Backbone, just stick with those for now; not completely dreadful, anyway.

Now onto code:

You want a flat structure, else you're missing the point of Promises. It's not wise to nest promises, necessarily, because Promises solve what nested async callbacks (callback hell) could not.

You will save yourself time and energy, and produce less buggy code by simply using a more readable code structure. It's not everything, but it's part of the game, so to speak.

> Promises are about making asynchronous code retain most of the lost properties > of synchronous code such as flat indentation and one exception > channel. > > -- Petka Antonov (Bluebird Promise Library)

// run async #1
asyncGetFn()
// first 'then' - execute more async code as an arg, or just accept results
// and do some other ops
.then(response => {
    // ...operate on response data...or pass data onto next promise, if needed
})
// run async #2
.then(asyncGetAnotherFn)
.then(response => {
    // ...operate on response data...or pass data onto next promise, if needed
})
// flat promise chain, followed by 'catch'
// this is sexy error handling for every 'then' above
.catch(err => {  
  console.error('Request failed', err) 
  // ...raise exeption...
  // ... or, retry promise... 
})

Solution 4 - Javascript

I didn't saw an answer with the syntactic sugar of async/await, so I'm posting my answer.

Another way to fetch "inside" another fetch in javascript is like -

try {
    const response = await fetch(url, {method: 'get'});
    const data = response.json();
    //use the data...
    const anotherResponse = await fetch(url, {method: 'get'});
    const anotherdata = anotherResponse.json();
    //use the anotherdata...
 } catch (error) {
    console.log('Request failed', error) ;
 }

So actually you call url after url one by one.

This code will work inside async context.

Solution 5 - Javascript

> Is it wisely using a code like this in javascript?

Yes. Your code is fine.
Except that after the second request, fetch(anotherUrl).then(function(response) {, I would replace return response.json(); with response.json().then(function(data2) { – just as after the first request.
The variable data2 will then contain the response body of the inner URL request, as desired.
This means that – whatever you want to do with data2, you must do it inside this second callback (since you don't return a promise.)
Also, a few more printouts will help to understand what is happening.

1. The original code – slightly modified

After making those changes, here is a Stack Snippet containing your code: 1

const url = 'https://jsonplaceholder.typicode.com/todos/1';; const anotherUrl = 'https://jsonplaceholder.typicode.com/todos/4';; fetch(url, { method: 'get' }).then(function (response) { response.json().then(function (data) { console.log('Response body of outer "url":'); console.log(JSON.stringify(data) + '\n\n'); fetch(anotherUrl).then(function (response) { response.json().then(function (data2) { console.log('Response body of inner "anotherUrl":'); console.log(JSON.stringify(data2) + '\n\n'); }); }).catch(function () { console.log('Booo'); }); }); }) .catch(function (error) { console.log('Request failed', error); }); .as-console-wrapper { max-height: 100% !important; top: 0; }

which is fine really, although the fat arrow style is more common these days for defining a function.

2. The code refactored

Here is a refactored version of your code. It has an inner chained/nested request – fetch(urlInner) – that depends on data retrieved from a previous/outer request: fetch (urlOuter).
By returning the promises of both the outer and the inner URL fetches, it is possible to access/resolve the promised result later in the code: 2

const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';; let urlInner = ''; const resultPromise = fetch(urlOuter) .then(responseO => responseO.json()) .then(responseBodyO => { console.log('The response body of the outer request:'); console.log(JSON.stringify(responseBodyO) + '\n\n'); const neededValue = responseBodyO.id + 3; urlInner = 'https://jsonplaceholder.typicode.com/todos/'; + neededValue; console.log('neededValue=' + neededValue + ', URL=' + urlInner); return fetch(urlInner) .then(responseI => responseI.json()) .then(responseBodyI => { console.log('The response body of the inner/nested request:'); console.log(JSON.stringify(responseBodyI) + '\n\n'); return responseBodyI; }).catch(err => { console.error('Failed to fetch - ' + urlInner); console.error(err); }); }).catch(err => { console.error('Failed to fetch - ' + urlOuter); console.error(err); });

resultPromise.then(jsonResult => {
  console.log('Result - the title is "' + jsonResult.title + '".');
});

.as-console-wrapper { max-height: 100% !important; top: 0; }

Note that no indentation is deeper than eight spaces.

3. Advantages of this code style

This is clearly a nested style of writing the code – meaning that the chained request fetch(urlInner) is indented and made inside the callback of the first request fetch(urlOuter). Yet, the indentation tree is reasonable, and this style resonates well with my intuition about chaining requests. – But more importantly, this style makes it possible to write error messages that pinpoint which URL failed.

Run the snippet below to see how the error message tells that it is the inner/second URL that causes the error:

const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';; let urlInner = ''; const resultPromise = fetch(urlOuter) .then(responseO => responseO.json()) .then(responseBodyO => { console.log('The response body of the outer request:'); console.log(JSON.stringify(responseBodyO) + '\n\n'); const neededValue = responseBodyO.id + 3; urlInner = 'https://VERY-BAD-URL.typicode.com/todos/'; + neededValue; console.log('neededValue=' + neededValue + ', URL=' + urlInner); return fetch(urlInner) .then(responseI => responseI.json()) .then(responseBodyI => { console.log('The response body of the inner/nested request:'); console.log(JSON.stringify(responseBodyI) + '\n\n'); return responseBodyI; }).catch(err => { console.error('Failed to fetch - ' + urlInner); console.error(err); }); }).catch(err => { console.error('Failed to fetch - ' + urlOuter); console.error(err); });

resultPromise.then(jsonResult => {
  console.log('Result - the title is "' + jsonResult.title + '".');
});

.as-console-wrapper { max-height: 100% !important; top: 0; }

4. Flattening all occurrences of .then()?

Inspired by others, you may be tempted to flatten all occurrences of .then(), like below.

I would advise against doing this – or at least think twice before doing it. Why?

  • In the absence of errors, it doesn't matter.
  • If there are errors, such a style will force less distinct error messages:

const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';; let urlInner = ''; const resultPromise = fetch(urlOuter) .then(responseO => responseO.json()) .then(responseBodyO => { console.log('The response body of the outer request:'); console.log(JSON.stringify(responseBodyO) + '\n\n'); const neededValue = responseBodyO.id + 3; urlInner = 'https://VERY-BAD-URL.typicode.com/todos/'; + neededValue; console.log('neededValue=' + neededValue + ', URL=' + urlInner); return fetch(urlInner); }) .then(responseI => responseI.json()) .then(responseBodyI => { console.log('The response body of the inner/nested request:'); console.log(JSON.stringify(responseBodyI) + '\n\n'); return responseBodyI; }).catch(err => { console.error('Failed to fetch one or more of these URLs:'); console.log(urlOuter); console.log(urlInner); console.log(err); }); .as-console-wrapper { max-height: 100% !important; top: 0; }

The code is nicely flat, but the error caught at the end cannot decide which of the URL requests that failed.


1 All snippets of this answer comply with the JavaScript Semistandard Style.
2 About line 11 – return fetch(urlInner) – it is very easy to forget to return the fetch. (I once forgot it even after writing this answer.) If you do forget it, resultPromise will not contain any promise at all. The last three lines in the snippet will then fail – they will output nothing. The result fails completely!

Solution 6 - Javascript

I suggest using axios, is much better and you don't have to deal with JSON format. Also, the code looks cleaner and is easier to understand.

axios.get(firstUrl).then((resp1) => {
   // Handle success of first api
   axios.get(secondUrl).then((resp2) => {
      return resp2.data                 
   }).catch((error) => { /* Handle error of second api */ });
}).catch((error) => { /* Handle error of first api */ });

Blockquote from LogRocket.com:

> As with Fetch, Axios is promise-based. However, it provides a more > powerful and flexible feature set. > > Advantages of using Axios over the native Fetch API include: > > - Request and response interception > - Streamlined error handling > - Protection against XSRF > - Support for upload progress > - Response timeout > - The ability to cancel requests > - Support for older browsers > - Automatic JSON data transformation

Solution 7 - Javascript

I would use either an array of fetches instead or an array of urls, both in the order you would like to execute them. Then use reduce to execute them in sequence. This way it is much more scalable.

const urls = [
  'https://api.spacexdata.com/v4/launches/latest',
  'https://api.spacexdata.com/v4/launches/latest',
  'https://api.spacexdata.com/v4/launches/latest'
];

// handle the fetch logic
// and handle errors
const handleFetch = async (url) => {
  const resp = await fetch(url).catch(console.error);
  return resp.json()
}

// reduce fetches, receives the response
// of the previous, log it (and maybe use it as input)
const reduceFetch = async (acc, curr) => {
  const prev = await acc;
  console.log('previous call:', prev);

  return handleFetch(curr);
}

const pipeFetch = async urls => urls.reduce(reduceFetch, Promise.resolve(''));

pipeFetch(urls).then(console.log);

Solution 8 - Javascript

just some ways of doing it.

1, using async -await

 app.get("/getemployeedetails/:id", async (req, res) => {
  const id = req.params.id;
  const employeeUrl = "http://localhost:3000/employee/" + id;
  try {
    const response = await fetch(employeeUrl);
    const employee = await response.json();

    const projectUrl = "http://localhost:3000/project/" + employee.project_id;
    const response1 = await fetch(projectUrl);
    const project = await response1.json();

    const result = {
      ...employee,
      ...project,
    };
    res.send(result);
  } catch (error) {
    console.log("getData: ", error);
  }
});

2, chaining of then

app.get("/getemployeedetails/:id", (req, res) => {
  const id = req.params.id;
  const employeeUrl = "http://localhost:3000/employee/" + id;
  let employeeResponse = null;
  fetch(employeeUrl)
    .then((employee) => employee.json())
    .then((resp) => {
        employeeResponse = resp
      const projectUrl =
        "http://localhost:3000/project/" + employeeResponse.project_id;
      return fetch(projectUrl);
    })
    .then((project) => project.json())
    .then((projectResponse) => {
      const result = {
        ...employeeResponse,
        ...projectResponse,
      };
      res.send(result);
    })
    .catch((err) => console.log(err));
});

3, chaining in a better way

app.get("/getemployeedetails/:id", (req, res) => {
  const id = req.params.id;
  getEmployeeResponse(id).then((employeeResponse) => {
    getProjectResponse(employeeResponse.project_id)
      .then((projectResponse) => {
        const result = {
          ...employeeResponse,
          ...projectResponse,
        };
        res.send(result);
      })
      .catch((err) => console.log(err));
  });
});

function getEmployeeResponse(id) {
  return new Promise((resolve, reject) => {
    const employeeUrl = "http://localhost:3000/employee/" + id;
    fetch(employeeUrl)
      .then((employee) => employee.json())
      .then((resp) => resolve(resp))
      .catch((err) => reject(err));
  });
}

function getProjectResponse(id) {
  return new Promise((resolve, reject) => {
    const projectUrl = "http://localhost:3000/project/" + id;
    fetch(projectUrl)
      .then((project) => project.json())
      .then((resp) => resolve(resp))
      .catch((err) => reject(err));
  });
}

you decide.

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
QuestionDvlprView Question on Stackoverflow
Solution 1 - JavascriptOri DroriView Answer on Stackoverflow
Solution 2 - Javascriptguest271314View Answer on Stackoverflow
Solution 3 - Javascriptjeremiah.treinView Answer on Stackoverflow
Solution 4 - JavascriptTamir AdlerView Answer on Stackoverflow
Solution 5 - JavascriptHenkeView Answer on Stackoverflow
Solution 6 - JavascriptGassView Answer on Stackoverflow
Solution 7 - JavascriptRobert-Jan KuyperView Answer on Stackoverflow
Solution 8 - JavascriptSafnasView Answer on Stackoverflow