node.jsasynchronouswaterfall

How to make API calls using Async waterfall in nodejs


In my application i have to perform a series of API calls step by step. I tried to achieve this using the async waterfall option .But before getting the response of the first API, second function is getting executed and same thing is happening in the second function also. That is before getting the response, final result is send . If i try to perform some task other than API calls , the waterfall operation is happening properly. Below is the code i have tried. For testing purpose same API is called from both functions (myFirstFunction, mySecondFunction).

const async = require('async');
router.get('/', (req, res) => {
    async.waterfall([
        myFirstFunction,
        mySecondFunction,
    ],
        function (err, result) {
            if (err) {
                console.log("Error-->" + JSON.stringify(err));
                res.status(400).json(err).end();
            } else {
                console.log(" Result -->" + JSON.stringify(result));
                res.status(200).json("Success").end();
            }
        });

});

const myFirstFunction = (callback) => {
    console.log(" ------- myFirstFunction ");
    const vehList = callVehicle();
    console.log("First Function -->" + JSON.stringify(vehList));
    callback(null, vehList);
}
const mySecondFunction = (vehList, callback) => {
    console.log("-------- mySecondFunction");
    const vehList1 = callVehicle();
    const vehList2 = {
        "1": vehList,
        "2": vehList1
    }
    console.log("Second Function -->" + JSON.stringify(vehList2));
    callback(null, vehList2);
}

const callVehicle = () => {
    var options = {
        method: "GET",
        json: true,
        strictSSL: false,
        url: `http://localhost:8080/vehicle/make`
    };
    request(options, function(error, response, body) {
        if (body){
          console.log("Success REST Response: ", JSON.stringify(body));
          return body;
        } else {
          console.log("Error : ", JSON.stringify(error));
          return {"Error": "error"};
        }
      });
}

Output obtained

F:\workSpace_Node\SampleApp>node app.js
server running at 9086
 ------- myFirstFunction
First Function -->undefined
-------- mySecondFunction
Second Function -->{}
 Result -->{}
Success REST Response:  {"vehicleList":[{"make":"Audi","model":"A3","vin":"QVFCFQT7894563214"},{"make":"Audi","model":"A4","vin":"ASECFQT7894563214"},{"make":"Audi","model":"Q7"},{"make":"Audi","model":"Q5","vin":"QWECFQT7894993214"}]}
Success REST Response:  {"vehicleList":[{"make":"Audi","model":"A3","vin":"QVFCFQT7894563214"},{"make":"Audi","model":"A4","vin":"ASECFQT7894563214"},{"make":"Audi","model":"Q7"},{"make":"Audi","model":"Q5","vin":"QWECFQT7894993214"}]}

How to achieve this using async.waterfall or is there any better approach for this requirement.


Solution

  • The best way for me to use Promises and asynchronous functions.

    But if you want to do it with without promises, I think that all of your code that is asynchronous should get a callback parameter.

    But your callVehicle has not callback parameter, so parent function cannot be notified when call Vehicle took a response.

    const myFirstFunction = (callback) => {
        callVehicle(callback);
    }
    const mySecondFunction = (vehList, callback) => {
        const vehList1 = callVehicle((err, res) => callback (err, {
        1: vehList,
         2: res
      }));
    }
    // We add callback that should be called when we have a result of the api request
    const callVehicle = (callback) => {
        var options = {
            method: "GET",
            json: true,
            strictSSL: false,
            url: `http://localhost:8080/vehicle/make`
        };
        request(options, function(error, response, body) {
            if (!error && body){
              console.log("Success REST Response: ", JSON.stringify(body));
              callback(null, body)
            } else {
              console.log("Error : ", JSON.stringify(error));
              callback({ Error: error }, null)
          });
    }
    

    With promises:

    const get = (options) => new Promise(
      (resolve, reject) => request(
        {method: 'GET', ...options},
         (err, response, body)=> err ? reject(err) : resolve(body)
      )
    )
    
    const callVehicle = () => get({
      json: true,
      strictSSL: false,
      url: `http://localhost:8080/vehicle/make`
    })
    
    router.get('/', async (req, res) => {
      try {
        const firstVehicle = await callVehicle()
        const secondVehicle = await callVehicle()
        res.status(200).json("Success").end();
      } (error) {
        res.status(400).json(error).end();
      }
    });