Axios: chaining multiple API requests

ReactjsReact NativeAxios

Reactjs Problem Overview


I need to chain a few API requests from the Google Maps API, and I'm trying to do it with Axios.

Here is the first request, which is in componentWillMount()

axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p1)
  .then(response => this.setState({ p1Location: response.data }))  }

Here is the second request:

axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p2)
  .then(response => this.setState({ p2Location: response.data }))

Then we have a third request, which is dependent on the first two being completed:

axios.get('https://maps.googleapis.com/maps/api/directions/json?origin=place_id:' + this.state.p1Location.results.place_id + '&destination=place_id:' + this.state.p2Location.results.place_id + '&key=' + 'API-KEY-HIDDEN')
  .then(response => this.setState({ route: response.data }))

How can I chain these three calls so that the third happens after the first two?

Reactjs Solutions


Solution 1 - Reactjs

First off, not sure you want to do this in your componentWillMount, it's better to have it in componentDidMount and have some default states that will update once done with these requests. Second, you want to limit the number of setStates you write because they might cause additional re-renders, here is a solution using async/await:

async componentDidMount() {

  // Make first two requests
  const [firstResponse, secondResponse] = await Promise.all([
    axios.get(`https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p1}`),
    axios.get(`https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p2}`)
  ]);

  // Make third request using responses from the first two
  const thirdResponse = await axios.get('https://maps.googleapis.com/maps/api/directions/json?origin=place_id:' + firstResponse.data.results.place_id + '&destination=place_id:' + secondResponse.data.results.place_id + '&key=' + 'API-KEY-HIDDEN');

  // Update state once with all 3 responses
  this.setState({
    p1Location: firstResponse.data,
    p2Location: secondResponse.data,
    route: thirdResponse.data,
  });

}

Solution 2 - Reactjs

A little late to the party, but I like this pattern of chaining promises, returning them to keep the promise chain alive.

axios
  .get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p1)
  .then(response => {
    this.setState({ p1Location: response.data });
    return axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p2);
  })
  .then(response => {
    this.setState({ p2Location: response.data });
    return axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p3);
  })
  .then(response => {
    this.setState({ p3Location: response.data });
  }).catch(error => console.log(error.response));

Solution 3 - Reactjs

Have you used axios.all ? You can try with something similar:

axios.all([axios.get(`firstrequest`),
           axios.get(`secondrequest`),
           axios.get(`thirdrequest`)])
     .then(axios.spread((firstResponse, secondResponse, thirdResponse) => {  
         console.log(firstResponse.data,secondResponse.data, thirdResponse.data);
     }))
     .catch(error => console.log(error));

This will take all your get and will put it inside a response that has to be called with .data like: firstResponse.data

Solution 4 - Reactjs

For better performance and cleaner code:

  1. Use promise.all() or axios.all() to execute request1 and request2 at the same time. So request2 will execute without waiting for request1 response. After request1 and request2 return the response, request3 will continue execute based on the returned response data as parameter.

  2. Template Strings use back-ticks (``)

    async componentDidMount(){ try{ const [request1, request2] = await Promise.all([ axios.get(https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p1}), axios.get(https://maps.googleapis.com/maps/api/geocode/json?&address=${this.props.p2}) ]);

         const request3 = await axios.get(`https://maps.googleapis.com/maps/api/directions/json?origin=place_id:${request1.data.results.place_id}&destination=place_id:${request2.data.results.place_id}&key=${API-KEY-HIDDEN}`);
         console.log(request3);
     }
     catch(err){
         console.log(err)
     }
    

    }

Solution 5 - Reactjs

I think you need something like this:

const firstRequest = axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p1)
      .then(response => this.setState({ p1Location: response.data }))  }

const secondRequest = axios.get('https://maps.googleapis.com/maps/api/geocode/json?&address=' + this.props.p2)
  .then(response => this.setState({ p2Location: response.data }))

const thirdRequest = axios.get('https://maps.googleapis.com/maps/api/directions/json?origin=place_id:' + this.state.p1Location.results.place_id + '&destination=place_id:' + this.state.p2Location.results.place_id + '&key=' + 'API-KEY-HIDDEN')
  .then(response => this.setState({ route: response.data }))


Promise.all([firstRequest, secondRequest])
       .then(() => {
           return thirdRequest
       })

Solution 6 - Reactjs

For simultaneous requests with axios, you can use axios.all() plus axios.spread()

axios.spread() is used to spread the array of arguments into multiple arguments, so that all data can be passed to the function.

Example

const url_1 = '', url_2 = ''; 

axios.all([
  axios.get(url_1), 
  axios.get(url_2)
])
  .then(
     axios.spread((resp1, resp2) => {
       let id_1 = resp1.data.results.place_id
       let id_2 = resp2.data.results.place_id
       let url_3 = ''                          // <---- Build your third URL here
       axios.get(url_3)
         .then((resp3) => {
             // You have all the data available here to useState()
         })
     })
  )
  .catch((error) => console.log(error)) 

Solution 7 - Reactjs

This is related to JS's Promises. You can solve it in different ways. The simplest way to me is that you should nest each request starting from first to third. That means starting from the first request, you should put your second axios.get(url) into the first request's .then() and put the third request into the second request's .then().

For promises in general you expect that inside the .then() part promise is resolved and you can have access to the response. So that by nesting, you can solve the problem of being asynchronous in a not so elegant way.

Solution 8 - Reactjs

create array of promise and then use reduce.

/**
 * Runs promises from array of functions that can return promises
 * in chained manner
 *
 * @param {array} arr - promise arr
 * @return {Object} promise object
 */
function runPromiseInSequence(arr, input) {
  return arr.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(input)
  )
}

// promise function 1
function p1(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 5)
  })
}

// promise function 2
function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2)
  })
}

// function 3  - will be wrapped in a resolved promise by .then()
function f3(a) {
 return a * 3
}

// promise function 4
function p4(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 4)
  })
}

const promiseArr = [p1, p2, f3, p4]
runPromiseInSequence(promiseArr, 10)
  .then(console.log)   // 1200

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
QuestionFreddyView Question on Stackoverflow
Solution 1 - ReactjsMatt AftView Answer on Stackoverflow
Solution 2 - ReactjschriszView Answer on Stackoverflow
Solution 3 - ReactjsRicardo GonzalezView Answer on Stackoverflow
Solution 4 - ReactjsToh Ban SoonView Answer on Stackoverflow
Solution 5 - ReactjsMorleeeView Answer on Stackoverflow
Solution 6 - ReactjsGassView Answer on Stackoverflow
Solution 7 - ReactjsmilkersaracView Answer on Stackoverflow
Solution 8 - ReactjsJashu ChaudharyView Answer on Stackoverflow