HTTP request in separate function[node.js/expressjs] - javascript

By using async module I have something like this and it works perfectly.
But when i try to restructure the code or make it reusable, it finish executing before it finished the HTTP requests. Nodejs do lots of things in asynchronous way so finding a solution to this is bit hard to me.
What I have up to now.
var async = require('async'),
http = require('http');
exports.unitedStates = function(req, res) {
var texas = {
//GET method data here / ex: host, path, headers....
};
var washington = {
//GET method data here / ex: host, path, headers....
};
async.parallel({
getSource: function(callback) {
http.request(texas, function(respond) {
//Http request
}).end();
},
getScreen: function(callback) {
http.request(washington, function(respond) {
//Http request
}).end();
}
},
function(err, results) {
//Return the results
/* REPLY TO THE REQUEST */
res.send( /* data here */ );
});
}
is there a perfect way to make this piece of code to reusable?
Example
exports.unitedStates = function(req, res) {
var tokyo = japan();
//send the result to front end
res.send(tokyo);
}
function japan(){
//async calls comes here and return the value...
return result;
}

Instead of returning value from function, pass a callback.
exports.unitedStates = function (req, res) {
// pass callback here
japan(function (value) {
res.send(value);
});
}
function japan(cb) {
//async call here
cb(result);
}

Related

Node.js and Express: wait for asynchronous operation before responding to HTTP request

Say there is a HTTP GET callback defined as:
router.get('/latestpost', function(req, res, next) {
var data = new FbData();
get_latest_post (data);
get_post_image (data);
res.json(data);
};
Both get_ functions use the fb package to generate a HTTP request and execute a callback when finished. How can the above GET callback be modified in order to wait for the responses from Facebook and only then send a response to the client?
At the time being I solved the problem by executing the get_ functions in series and passing them the res (response) argument, with the last function sending the response:
router.get('/latestpost', function(req, res, next) {
var data = new FbData();
get_latest_post (res, data);
};
function get_latest_post (res, data) {
FB.api(_url, function (res_fb) {
if(!res_fb || res_fb.error) {
console.log(!res_fb ? 'error occurred' : res_fb.error);
return;
}
// Do stuff with data
get_post_image (res, data);
});
}
function get_post_image (res, data) {
FB.api(_url, function (res_fb) {
if(!res_fb || res_fb.error) {
console.log(!res_fb ? 'error occurred' : res_fb.error);
return;
}
// Do stuff with data
/* At the end send the post data to the client */
res.json(data);
});
}
I have found a similar question, but I'm wrapping my head around it, since I can't find a proper way to apply the solution to my problem. I have tried using the patterns described in this manual, but I can't get it to execute using promises, or async/await. Can someone please point me in the right direction?
Your API can easily be modified to return a promise:
function get_post_image (res, data) {
return new Promise((resolve, reject) => {
FB.api(_url, function (res_fb) {
if(!res_fb || res_fb.error) {
reject(res_fb && res_fb.error);
} else resolve(res_fb/*?*/);
});
}
Now that you have a promise, you can await it:
router.get('/latestpost', async function(req, res, next) {
const data = new FbData();
const image = await get_post_image (data);
res.json(data);
});

Node.js / Express.js can't get body response from API call via Request middleware

I'm trying to learn Node.js via Express.js framework. Currently I need to make an API call to get some data usefull for my app.
The API call is made with Request middleware, but when I'm out of the request my variable become undefined ... Let me show you :
var request = require('request');
var apiKey = "FOOFOO-FOOFOO-FOO-FOO-FOOFOO-FOOFOO";
var characters = [];
var gw2data;
var i = 0;
module.exports.account = function() {
request('https://api.guildwars2.com/v2/characters/?access_token=' + apiKey, function (error, response, body) {
gw2data = JSON.parse(body);
console.log('out request ' + gw2data); // {name1, name2 ...}
for (i; i < gw2data.length; i++) {
getCharacInfo(gw2data[i], i);
}
});
console.log('out request ' + characters); // undefined
return characters;
};
function getCharacInfo (name, position) {
request('https://api.guildwars2.com/v2/characters/' + name + '/?access_token=' + apiKey, function (error, response, body) {
if (!error && response.statusCode == 200) {
characters[position] = JSON.parse(body);
}
});
}
I don't understand why the gw2data variable become undefined when I go out of the request ... Someone can explain me ?
EDIT : I come to you because my problem has changed, I now need to make an API call loop in my first API call, same async problem I guess.
The previous code has evoluate with previous answers :
module.exports.account = function(cb) {
var request = require('request');
var apiKey = "FOOFOO-FOOFOO-FOO-FOO-FOOFOO-FOOFOO";
var characters = [];
var i = 0;
request('https://api.guildwars2.com/v2/characters/?access_token=' + apiKey, function(err, res, body) {
var numCompletedCalls = 1;
for (i; i < JSON.parse(body).length; i++) {
if (numCompletedCalls == JSON.parse(body).length) {
try {
console.log(characters); // {} empty array
return cb(null, characters);
} catch (e) {
return cb(e);
}
}else {
getCharacInfo(JSON.parse(body)[i]);
}
numCompletedCalls++;
}
});
};
function getCharacInfo (name) {
request('https://api.guildwars2.com/v2/characters/' + name + '/?access_token=' + apiKey, function (err, res, body) {
if (!err) {
console.log(characters); // {data ...}
characters.push(JSON.parse(body));
}
});
}
And the call of this function :
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
var charactersInfos = require('../models/account');
charactersInfos.account(function(err, gw2data) {
if (err) {
console.error('request failed:', err.message);
} else {
res.render('index', {
persos : gw2data
});
}
});
});
The problem is, when I return the characters array, it's always an empty array, but when I check in my function getCharacInfo, the array contains the data ...
The reason gw2data is undefined in the second console.log is because your logging it too early. request is an asynchronous operation therefore it will return immediately...however, that doesn't mean the callback will.
So basically what your doing is logging gw2data before it's actually been set. When dealing with asynchronous operations the best approach is to generally make your own method asynchronous as well which can be accomplished in a couple of ways - the simplest being having your function accept a callback which expects the data in an asynchronous way e.g.
module.exports.account = function(cb) {
request(..., function(err, res, body) {
// return an error if `request` fails
if (err) return cb(err);
try {
return cb(null, JSON.parse(body));
} catch (e) {
// return an error if `JSON.parse` fails
return cb(e);
}
});
}
...
var myModule = require('mymodule');
myModule.account(function(err, g2wdata) {
if (err) {
console.error('request failed', err.message);
} else {
console.log('out request', g2wdata);
}
});
Based on your syntax I'm assuming you aren't working with ES6, worth looking at this (particularly if your just starting to learn). With built-in promises & also async-await support coming relatively soon this sort of stuff becomes relatively straightforward (at least at the calling end) e.g.
export default class MyModule {
account() {
// return a promise to the caller
return new Promise((resolve, reject) => {
// invoke request the same way but this time resolve/reject the promise
request(..., function(err, res, body) {
if (err) return reject(err);
try {
return resolve(JSON.parse(body));
} catch (e) {
return reject(e);
}
});
});
}
}
...
import myModule from 'mymodule';
// handle using promises
myModule.account()
.then(gw2data => console.log('out request', gw2data))
.catch(e => console.error('request failed', e));
// handle using ES7 async/await
// NOTE - self-executing function wrapper required for async support if using at top level,
// if using inside another function you can just mark that function as async instead
(async () => {
try {
const gw2data = await myModule.account();
console.log('out request', gw2data);
} catch (e) {
console.log('request failed', e);
}
})();
Finally, should you do decide to go down the Promise route, there are a couple of libraries out there that can polyfill Promise support into existing libraries. One example I can think of is Bluebird, which from experience I know works with request. Combine that with ES6 you get a pretty neat developer experience.
The request API is asynchronous, as is pretty much all I/O in Node.js. Check out the Promise API which is IMO the simplest way to write async code. There are a few Promise adapters for request, or you could use something like superagent with promise support.

Node.js wait until request is done

I'm trying to make a simple app that requests some data from an API. I'm building it with express and I have the following code that does the request:
module.exports = {
getPrevMatches: function(){
request({
uri: 'http://worldcup.sfg.io/matches',
json: true
}, this.process);
},
process: function(error, response, body){
if(response.statusCode === 200){
return body;
}
}
}
And the following in my express script:
app.get('/previous', function (req, res){
var prevMatches = worldcup.getPrevMatches();
console.log(prevMatches);
res.render('prev.html', {
prevMatches: prevMatches
});
});
prevMatches is always undefined at this point. I thought that the request package would wait until, well, the request was finished and then proceed with my code. Is this not the case?
Thanks.
Using a callback based approach (like stated in the comments):
function getPrevMatches(cb) {
request({
uri: 'http://worldcup.sfg.io/matches',
json: true
}, function(error, response, body){
if (response.statusCode === 200){
cb(body);
}
// Do error handling here!
});
}
module.exports = {
getPrevMatches: getPrevMatches
}
As you can see there is no need to export the process function. The calling code now becomes:
app.get('/previous', function(req, res){
worldcup.getPrevMatches(function(prevMatches) {
console.log(prevMatches);
res.render('prev.html', {
prevMatches: prevMatches
});
});
});
First remark: you still have to handle the errors from the request call (added as a comment).
Second remark: you might want to like at a Promises based approach to avoid callback hell. Request has a very nice promises based wrapper called conveniently request-promise
It is a good use case for promises.
There are many libs, you can use https://www.npmjs.com/package/request-promise for example.
var rp = require('request-promise');
module.exports = {
getPrevMatches: function(){
return rp({
uri: 'http://worldcup.sfg.io/matches',
json: true
});
}
}
I'm not sure if this.process would work in this context
app.get('/previous', function (req, res){
var prevMatches = worldcup.getPrevMatches();
prevMatches.then(function (data) {
console.log(data);
res.render('prev.html', {
prevMatches: data
});
})
prevMatches.catch(function (e) {
res.status(500, {
error: e
});
});
});

how to write unit test to "request" node module with sinon.stub?

I have this function in my code :
let request = require("request");
let getDrillDownData = function (userId, query, callback) {
query.id = userId;
let urlQuery = buildUrlFromQuery(query);
request.get({
url: urlQuery,
json: true
}, function (error, response, data) {
if (!error && response.statusCode === 200) {
return callback(null, calculateExtraData(data));
} else if (error) {
return callback(error, null);
}
});
};
and I wish to write some unit test which verify that when the function is called with correct parameters, it is running OK,
and if there is an error, it did return the error
I wrote this unit test code :
describe.only('Server Service Unit Test', function(){
var sinon = require('sinon'),
rewire = require('rewire');
var reportService;
var reportData = require('./reportData.json');
beforeEach(function(){
reportService = rewire('../../services/reports.server.service');
});
describe('report methods', function(){
var reportData;
var query = { id: "test"};
var userId = 'testuser';
var getDrillDownData;
var request;
beforeEach(function(){
getDrillDownData = reportService.__get__('getDrillDownData');
});
it ('should get drill down data by userId and query', function(done){
var getCallback = sinon.stub();
request = {
get: sinon.stub().withArgs({
url: query,
json: true
}, getCallback.withArgs("error", {statusCode: 200}, reportData))
};
reportService.__set__('request', request);
getDrillDownData(userId, query, function(err, returnData){
(err === null).should.eql(true);
//(getCallback.withArgs(undefined, {statusCode: 200}, reportData).calledOnce).equal(true);
done();
});
});
});
But I keep getting this error:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
Can someone help?
Thanks
I would stub request.get() directly:
describe('report methods', function() {
// Make `request.get()` a Sinon stub.
beforeEach(function() {
sinon.stub(request, 'get');
});
// Restore the original function.
afterEach(function() {
request.get.restore();
});
it ('should get drill down data by userId and query', function(done) {
// See text.
request.get.yields(null, { statusCode : 200 }, { foo : 'bar' });
// Call your function.
getDrillDownData('userId', {}, function(err, data) {
...your test cases here...
done();
});
});
});
Using request.get.yields() (which calls the first function argument that Sinon can find in the argument list; in this case, it's the (error, response, data) callback that gets passed to request.get() in your function) you can tell Sinon which arguments to use to call the callback.
That way, you can check if the callback to request.get() handles all arguments properly.
You can use .withArgs() too (request.get.withArgs(...).yields(...)), although you have to be sure that you're using it correctly; otherwise, if the exact arguments don't match, Sinon will call the original request.get() instead of using the stubbed version.
Instead, I prefer using stub.calledWith() to check for the correct arguments after the call has been made. That integrates much better with Mocha as well, as you can use explicit assertions.

Why doesn't my "required" function return anything? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
So i have this little Express.js Application, which accepts a POST-request with a "city" as body. The application processes the call and uses an external service for that. I tried to split up the "Pure" logic from the REST-controller to keep it clean, but somehow when i call the "weather" method, it won't return anything, even if the String, i am passing, is a valid object.
My guess is, that there is an issue with an asynchronious call, but i don't see myself in the position, to solve it myself.
RestController.js
module.exports = (function() {
var router = require('express').Router()
var bodyParser = require('body-parser')
var weather = require('./weather')
router.use(bodyParser.urlencoded({ extended: true }))
router.use(bodyParser.json())
//TODO DATABASE CONNECTION
//REST CONTROLLER
router.get('/', function(req, res) {
res.json({response: 'Hello world!'})
})
router.post('/weather', function(req, res) {
var r = weather(req.body.city)
res.send(r)
})
return router
})();
weather.js
var request = require('request')
module.exports = function(loc) {
request('http://api.openweathermap.org/data/2.5/weather?q='+loc+'&units=metric&APPID=API_TOKEN', function(err , ires) {
if(!err) {
json = JSON.stringify({temp: JSON.parse(ires.body).main.temp, name: JSON.parse(ires.body).name})
console.log(json)
return json
}
else {
return err
}
})
};
Best regards,
Tim
You should pass a callback to your weather function so you can handle it when it's complete. e.g.
module.exports = function(loc, callback) {
request('http://api.openweathermap.org/data/2.5/weather?q='+loc+'&units=metric&APPID=API_TOKEN', function(err , ires) {
if(!err) {
json = JSON.stringify({temp: JSON.parse(ires.body).main.temp, name: JSON.parse(ires.body).name})
console.log(json)
callback(null, json)
} else {
callback(err)
}
})
};
Then use as follows in your program:
router.post('/weather', function(req, res) {
var r = weather(req.body.city, function (e, result) {
if (e) return res.status(500).send({error: e});
res.send(result)
})
})
This is the standard node pattern (there are better ones). Node is non-blocking so it will not wait for your weather request to complete. It will eventually handle it on the event loop once the callback is finally invoked.

Categories