How to make API calls using Async waterfall in nodejs - node.js

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.

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();
}
});

Related

Null in request [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
In that code, console.log(game) gives me an array, but return(game) gives me null.
I don't know, what should I do to gain that array
takeApi: async (root, args, { req }, info) =>{
let userNick='Izyi';
var request = require('request');
var JsonFind=require('json-find');
var url = 'https://someapi/'+userNick;
var game;
request.get({
url: url,
json: true,
headers: {'API-KEY': 'XXX'}
}, (err, res, data) => {
if (err) {
console.log('Error:', err);
} else if (res.statusCode !== 200) {
console.log('Status:', res.statusCode);
} else {
const doc= JsonFind(data.lifeTimeStats);
var matchesPlayed=(doc.checkKey('7').value);
var wins=(doc.checkKey('8').value);
var kills=(doc.checkKey('10').value);
game ={kills:kills,wins:wins,matchesPlayed:matchesPlayed}
console.log(game);
return(game);
}
})
return(game);
}
request.get works via a callback and is not directly compatible with async/await. That callback happens when the request is done or has errored out. The return(game); then happens before the request has completed.
You need to return a new Promise and then resovle or reject based on the results passed to the callback.
You can then await or .then takeApi and expect to have a value returned.
const takeApi = async(root, args, { req }, info) => {
let userNick = 'Izyi';
var request = require('request');
var JsonFind = require('json-find');
var url = 'https://someapi/' + userNick;
// return a Promise, which will work
// by the called using `await` or `.then`
return new Promise((resolve, reject) => {
request.get({
url: url,
json: true,
headers: {
'API-KEY': 'XXX'
}
}, (err, res, data) => {
if (err) {
console.log('Error:', err);
// error, reject
reject(err);
} else if (res.statusCode !== 200) {
console.log('Status:', res.statusCode);
// error, reject
reject(res.statusCode);
} else {
const doc = JsonFind(data.lifeTimeStats);
var matchesPlayed = (doc.checkKey('7').value);
var wins = (doc.checkKey('8').value);
var kills = (doc.checkKey('10').value);
const game = {
kills: kills,
wins: wins,
matchesPlayed: matchesPlayed
}
console.log(game);
// success, resolve
resolve(game);
}
})
});
}

how to get data outside request

I have a scenario where i need to take response (body) of request method outside request. How can i do it?
request.post({
url: 'http://localhost/api/messages',
form: { key: message }
}, function (err, httpResponse, body) {
tmsg = body;
})
console.log(tmsg);
I need this tmsg outside for next processing, Actual scenario is as below.
app.post('/incomemsg', function (req, res) {
var mediaCount = req.body.NumMedia;
if (mediaCount == 0) {
//var twiml = getResponse(message);
var twiml = new MessagingResponse();
request.post({
url: 'http://localhost:3978/api/messages',
form: { key: message }
}, function (err, httpResponse, body) {
tmsg = body;
})
console.log(tmsg);
}else {
//dosomething which outputs tmsg
}
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(tmsg.toString());
});
The problem is you are trying to assign value to a global variable in request.post's callback() which is only called after request.post is executed by Asynchronous logic(API calls are all async), so a better way would be to promisify request.post and await the request.post to make it seem synchronous.
const requestPromisified = requestObject =>
new Promise((resolve, reject) => {
request.post(requestObject, function(err, httpResponse, body) {
if (err) {
reject(err);
}
resolve(body);
});
});
const body = await requestPromisified({
method: "POST",
url: "http://localhost/api/messages",
form: { key: message }
});
You only can do something with tmsg when you made the request so you need to rearrange your code like this:
app.post('/incomemsg', function (req, res) {
var mediaCount = req.body.NumMedia;
var twiml = new MessagingResponse();
request.post({
url: 'http://localhost:3978/api/messages',
form: { key: message }
}, function (err, httpResponse, body) {
tmsg = body;
console.log(tmsg);
if (mediaCount === 0) {
//do something with tmsg
} else {
//do something else with tmsg
}
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(tmsg.toString());
});
});
Otherwise tmsg will be null because there was no request made to fill that variable.

Making second api call after first api call returns data

I have two seperate functions that make API calls to different endpoints for return JSON Data using BlueBirdPromise.
const searchVenues = (type) => {
logger.debug('getVenues : type = ' + type);
const config = {
url: urlAPIServer + '/venue/available',
qs: {
type,
},
headers: {
'x-api-key': dataApiKey
}
};
return new BluebirdPromise((resolve, reject) => {
request.get(config, (err, response, body) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(JSON.parse(body));
}
});
});
};
const getVenuesWithCuisine = () => {
logger.debug('getVenuesWithCuisine');
const config = {
url: urlAPIServer + '/venue/viewvenuewithcuisine',
headers: {
'x-api-key': dataApiKey
}
};
return new BluebirdPromise((resolve, reject) => {
request.get(config, (err, response, body) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(JSON.parse(body));
}
});
});
};
Invoking the funtions seperately to get the data from the API and bind it to variable.
searchVenues(venueType).then((venues) => {
checkContextTimeout(context);
conversationContext.venueType = venueType;
conversationContext.venues = venues;
context.skill = conversationContext;
});
getVenuesWithCuisine().then((venueswithcuisines) => {
conversationContext.venue_details = venueswithcuisines[0}["venue_details"];
conversationContext.cuisines = venueswithcuisines[1]["cuisines"];
conversationContext.venueType = venueType;
conversationContext.venues = venuesJson.venues;
continueConversation(request, response, context);
});
The problem with above implementation is, if for some reason getVenuesWithCuisine completed first before searchVenues the continueConversation is getting invokes making conversationContext.venues = venues as null.
How can i make these API calls synchronous so that the second API call is made only after the first one returns data.
You can use Promise.all instead of doing it synchronously. That will resolve once both of your promises resolve and give you the results.
http://bluebirdjs.com/docs/api/promise.all.html
Promise.all([searchVenues(venueType), getVenuesWithCuisine()]).then(function([venueResp, cuisineResp]) {
...
});

How can I use the value of the body outside this method?

var request = require('request');
var boardsCall = {
method: 'GET',
url: 'https://api.trello.com/1/organizations/xxxxxxxxxx/boards?filter=open&fields=id,name',
qs: {
key: 'xxxxxxxxxxxxxxxx',
token: 'xxxxxxxxxxxxxxxxxxxxxxxxx'
}
};
function test(url, callback) {
request(url, function(error, response, body) {
if (error) {
return callback(error);
}
callback(null, JSON.parse(body));
})
}
const x = test(boardsCall, function(err, body) {
if (err) {
console.log(err);
}
else {
return body;
}
})
console.log(x);
how can I use the value of the body outside?
to use it in other methods later
I am open to any changes best practices, I read a lot and got a bit confused on the topic of callbacks, promises async await.
In my approach request is wrapped with Promise, test function returns Promise response. Inside the main method test function will be executed synchronously. Once the response value assigned to x do remaining processing logic inside the main() method.
var request = require('request');
var boardsCall = {
method: 'GET',
url: 'https://api.trello.com/1/organizations/xxxxxxxxxx/boards?filter=open&fields=id,name',
qs: {
key: 'xxxxxxxxxxxxxxxx',
token: 'xxxxxxxxxxxxxxxxxxxxxxxxx'
}
};
function test(url) {
//Wrapping request callback with Promise
return new Promise((res, rej)=> {
request(url, function(error, response, body) {
if (error) {
rej(error);
}
res(JSON.parse(body));
})
})
}
async function main() {
try {
const x = await test(boardsCall);
console.log("Result : ", x );
// Remaining logic to process based on x value
} catch(e) {
console.error("Error :", e);
}
}
//Calling main method
main()

Express - Nodejs external rest api call

I want to make a backend call to an external api's and populate my page with the results. What is the best way to do this?
The "request.get" call is asynchronous, so I understand the code below is erroneous. However, I have written it in that fashion so that I can explain what I want to actually do.
Further, I may have 5-6 external api, is there a way to make this asynchronous for every api but synchronous get call?
This is how my current code looks like:
var express = require('express');
var request = require('request');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
var body = getRawApiResponse("someURL");
console.log("Index >" + body);
res.render('index', { title: 'Express', api: "some", body: body});
});
function getRawApiResponse(api){
request.get({
uri: api,
},
function(error, response, body){
if (!error && response.statusCode === 200) {
console.log("Index > Raw Api Response: " + body);
} else {
console.log(error);
}
});
}
You can wrap getRawApiResponse() in a promise
function getRawApiResponse(api){
return new Promise(function(resolve, reject){
request.get({
uri: api,
},
function(error, response, body){
if (!error && response.statusCode === 200) {
resolve(body)
} else {
reject(error)
}
});
});
}
which resolves on success and rejects in case of an error then you can chain it inside the get request like
router.get('/', function(req, res, next) {
getRawApiResponse("someURL")
.then(function(body){
res.render('index', { title: 'Express', api: "some", body: body});
})
.catch(err){
// do something
}
});
Look into Promises or async/await. You can use them to call your async apis and wait for the response making the call synchronous.
http://bluebirdjs.com/docs/getting-started.html
A Sample code of async/ await which u can modify for ur purpose is as below:
try{
let orderDetails = await MongoHelper.findOneByCriteria(MongoCollections.ORDER,searchCriteria);
}catch(err){
return err;
}
MongoHelper.findOneByCriteria = (collectionName, criteria) => {
return new Promise((resolve, reject) => {
db.collection(collectionName).find(criteria).toArray()
.then((results) => {
if (results.length > 0) {
resolve(results[0]);
} else {
resolve(null);
}
});
});
}
The best way is to use Promises to avoid callbacks hell. If you can use node.js v7.6 or higher, it could be much easier with async/await.
router.get('/', function(req, res, next) {
getRawApiResponse("someURL")
.then(body => {
console.log("Index >" + body);
res.render('index', { title: 'Express', api: "some", body: body});
});
});
function getRawApiResponse(uri) {
return new Promise((resolve, reject) => {
request.get({ uri }, (error, response, body) => {
if (err) {
reject(err);
}
resolve(body);
});
});
}
In the example about I use promisification to return a promise from getRawApiResponse, but there is already a module which do the same https://github.com/request/request-promise.

Resources