Search code examples
javascriptnode.jshttprequest

How to do sequencial HTTP calls?


I have a couple of APIs I need to call to collect and merge information. I make the first API call and, based on the result, I make several calls to the second one (in a loop). Since http requests are asynchronous I'm loosing the information. By the time the second step is finished the server (nodejs) already sent the response back to the client.

I've already tried to, somehow, use the callback functions. This managed to keep the response to the client waiting but the information of the second call was still lost. I guess somehow the variables are not being synchronized. I also did a quick test with away/async but my Javascript mojo was not enough to make it run without errors.

/* pseudo code */
function getData(var1, callback){

url= "http://test.server/bla?param="+var1;

request.get(url, function (error, response, body){
var results = [];
  for(var item of JSON.parse(body).entity.resultArray) {  
     var o = {};
     o['data1'] = item.data1;
     o['data2'] = item.data2;
     o['data3'] = item.data3;

     getSecondStep(o, function(secondStepData){
       //console.log("Callback object");
       //console.log(o);
       o['secondStepData'] = secondStepData;
     });
     results.push(o);
  }
  callback(results);
});
}

function getSecondStep(object, callback){

  url = "http://othertest.server/foobar?param=" + object.data1;
  request.get(url, function (error, response, body){
    var results = [];
    if(response.statusCode == 200){
      for(var item of JSON.parse(body).object.array) {
        var o = {}
        o['data4'] = item.data4;
        o['data5'] = item.data5;
        results.push(o);
      }
      callback(results);
    }
});
}

What I would like is to be able to collect all the information into one JSON object to return it back to the client. The client will then be responsible for rendering it in a nice way.


Solution

  • I recommend using the async / await pattern with the request-promise-native library.

    This makes API calls really easy to make and the code is cleaner when using this pattern.

    In the example below I'm just calling a httpbin API to generate a UUID but the principle applies for any API.

    const rp = require('request-promise-native');
    
    async function callAPIs() {
        let firstAPIResponse = await rp("https://httpbin.org/uuid", { json: true });
        console.log("First API response: ", firstAPIResponse);
    
        // Call several times, we can switch on the first API response if we like.
        const callCount = 3;
        let promiseList = [...Array(callCount).keys()].map(() => rp("https://httpbin.org/uuid", { json: true }));
        let secondAPIResponses = await Promise.all(promiseList);
    
        return { firstAPIResponse: firstAPIResponse, secondAPIResponses: secondAPIResponses };
    }
    
    async function testAPIs() {
        let combinedResponse = await callAPIs();
        console.log("Combined response: " , combinedResponse);
    }
    
    testAPIs();
    

    In this simple example we get a combined response like so:

    {
        { 
            firstAPIResponse: { uuid: '640858f8-2e69-4c2b-8f2e-da8c68795f21' },
            secondAPIResponses: [ 
                { uuid: '202f9618-f646-49a2-8d30-4fe153e3c78a' },
                { uuid: '381b57db-2b7f-424a-9899-7e2f543867a8' },
                { uuid: '50facc6e-1d7c-41c6-aa0e-095915ae3070' } 
            ] 
        }
    }