I am writing a route that checks if a system app is online and then responds to the client with a simple 200 status ok, or a 404 status.
I'm using express and request to make the api call.
Route looks like this:
app.get('/status/keymgr', async (req, res, next) => {
try{
var endpoint = `http://${config.KeyManager.host}:${config.KeyManager.adminPort}/healthcheck`;
console.log(endpoint);
await request.get(endpoint, function(err, response, body){
if (!err && response.statusCode == 200){
res.send('OK');
}else{
res.status(404);
}
}).end();
}catch(error){
res.status(404);
}finally{
next();
}
});
For some reason, I am getting the following error:
uncaughtException: Can't set headers after they are sent.
I am guessing some kind of response is being sent to the browser before the route runs the res.send() or the res.status().
I can't figure out what's wrong here. Any idea??
AS #ndugger mentioned, the reason you are getting this exception is because request.get does not return a promise and hence await here is of no use. You have two options, either you use util.promisify or wrap your request under a new promise and resolve only when the callback finishes. Something like this
app.get('/status/keymgr', async (req, res, next) => {
var endpoint = `http://${config.KeyManager.host}:${config.KeyManager.adminPort}/healthcheck`;
console.log(endpoint);
try {
await new Promise((resolve, reject) => {
request.get(endpoint, function (err, response, body) {
if (!err && response.statusCode == 200) {
// res.send('OK');
resolve('OK');
} else {
reject('404')
// res.status(404);
}
});
});
res.send('OK');
} catch (err) {
res.status(404);
} finally {
next();
}
}
Related
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.
I'm trying to get a JSON response via the request method and return the output so that i can store it in a variable when the function is called. when i log the response within the request method, it works fine. However when i return the output, it doesn't return.
var getAPIresponse = function(url) {
var request = require('request');
request(url, function(error, response, body) {
if(!error && response.statusCode == 200) {
console.log(body); // WORKS PERFECTLY
return body; // I Believe the issue is here
} else {
console.log("Error: "+ error);
}
});
};
router.get('/', function (req, res) {
var poolList = getAPIresponse("www.addAURL");
console.log(poolList); // DOESN'T WORK. REPORTS AS UNDEFINED
res.render('index', model); // THIS IS JUST SAYS HELLO WORLD
});
What your method actually does is run the following two lines
var request = require('request');
request(url, function(error, response, body) {
...and then fall out of the function right away at which point your calling code gets undefined back. The callback isn't called until the request is done, which may be much later.
To make it work, your function needs a callback too that is called when the function is actually complete, something like;
var getAPIresponse = function(url, cb) {
var request = require('request');
request(url, function(error, response, body) {
if(!error && response.statusCode == 200) {
console.log(body); // WORKS PERFECTLY
} else {
console.log("Error: "+ error);
}
cb(error, body);
});
};
router.get('/', function (req, res) {
var poolList = getAPIresponse("www.addAURL", function(err, poolList) {
// This is run in a callback once the request is done.
console.log(poolList);
res.render('index', model);
});
});
Another way would be to use promises which can clean up the code somewhat when the number of callbacks is getting out of hand.
I have to scrape a web page for a key to use later as a cookie. That part works. But because the request is async the error is not handled. I want to make sure the error is passed along the middleware chain. But I can't get my brain around this one. Thanks for helping.
app.use('/', makeLoginCookie, function (req, res, next){
console.log('My app.use() starts here.');
//console.log('makeLoginCookie: %s', err);
next();
});
And here's the function
function makeLoginCookie(req, res, next) {
httpOptions = {
url: site.url,
headers: {
Cookie: null
}
}
// Get HIDDEN key, build login cookie
request(httpOptions, function(error, response, body) {
if (!error && response.statusCode == 200) {
//console.log(body)
var match = body.match( /\<INPUT TYPE=HIDDEN id=\"key\", value=\"([0-9a-f]+)\"\>/i );
var key = match[1]
var encrypted = sha1(key + site.username + site.password);
loginCookie = "username=" + key + ";password=" + encrypted;
next();
} else {
console.log("Connect failed %s" , error);
//err = new Error("Can't connect");
next();
}
});
};
Refer to Express Error handling, you can use next(err); to pass error in Express. Here is one good link.
I would use promises (Q library) in order to resolve this, and for another things too, specially for web scraping. Your "makeLoginCookie" function could return a deferred.promise and, when the request fails, reject it with the error.
Edit 1: I recommend this great video that explains how to work properly with async code https://www.youtube.com/watch?v=obaSQBBWZLk. It could help you with this and another stuff.
Edit 2: Using promises would be like this, see if it helps you:
var Q = require("q");
app.use('/', function (req, res, next) {
// This part, we will call your function "makeLoginCookie"
makeLoginCookie().then(function(){
// This is called when your function RESOLVES the promise
// Here you could log or do some stuff...
// Then, go next...
next();
}, function(error){
// This is called when your function REJECTS the promise
console.log("Connect failed %s" , error);
// And then, handle the error, like stopping the request and sending an error:
res.status(400).json({errorMessage: error});
})
}, function (req, res, next){
console.log('My app.use() starts here.');
next();
});
// Removed parameters from function
function makeLoginCookie() {
// This is the object that will return a promise
var deferred = Q.defer();
httpOptions = {
url: site.url,
headers: {
Cookie: null
}
}
// Get HIDDEN key, build login cookie
request(httpOptions, function(error, response, body) {
if (!error && response.statusCode == 200) {
//console.log(body)
var match = body.match( /\<INPUT TYPE=HIDDEN id=\"key\", value=\"([0-9a-f]+)\"\>/i );
var key = match[1]
var encrypted = sha1(key + site.username + site.password);
loginCookie = "username=" + key + ";password=" + encrypted;
// Instead of using next(), RESOLVE your promise
// next();
deferred.resolve(); // You can pass some data into it..
} else {
// Instead of using next(), REJECT your promise
// next();
deferred.reject(error); // You can pass some data into it, like an error object or string...
}
});
// Promise that something will be fulfilled or reject later
return deferred.promise;
};
There must have been some error elsewhere in my code because this works as expected now.
} else {
console.log("Connect failed %s" , error);
err = new Error("Can't connect");
next(err);
}
For a certain route, I have the following code:
router.get('/:id', function(req, res) {
var db = req.db;
var matches = db.get('matches');
var id = req.params.id;
matches.find({id: id}, function(err, obj){
if(!err) {
if(obj.length === 0) {
var games = Q.fcall(GetGames()).then(function(g) {
console.log("async back");
res.send(g);
}
, function(error) {
res.send(error);
});
}
...
});
The function GetGames is defined as follows:
function GetGames() {
var url= "my-url";
request(url, function(error, response, body) {
if(!error) {
console.log("Returned with code "+ response.statusCode);
return new Q(body);
}
});
}
I'm using the request module to send a HTTP GET request to my URL with appropriate parameter, etc.
When I load /:id, I see "Returned with code 200" logged, but "async back" is not logged. I'm also not sure that the response is being sent.
Once GetGames returns something, I want to be able to use that returned object in the route for /:id. Where am I going wrong?
Since GetGames is an async function write it in node.js callback pattern:
function GetGames(callback) {
var url= "my-url";
request(url, function(error, response, body) {
if(!error) {
console.log("Returned with code "+ response.statusCode);
return callback(null,body)
}
return callback(error,body)
});
}
Then use Q.nfcall to call the above function and get back a promise:
Q.nfcall(GetGames).then(function(g) {
})
.catch()
Can someone expound on the times when it's appropriate in a node.js Express app to throw an error like so:
throw new Error('my error');
or to pass this error on via the callback usually labelled 'next' like so:
next(error);
and could you please explain what each of them will do in the context of an Express app?
for example, here is an express function dealing with URL parameters:
app.param('lineup_id', function (req, res, next, lineup_id) {
// typically we might sanity check that user_id is of the right format
if (lineup_id == null) {
console.log('null lineup_id');
req.lineup = null;
return next(new Error("lineup_id is null"));
}
var user_id = app.getMainUser()._id;
var Lineup = app.mongooseModels.LineupModel.getNewLineup(app.system_db(), user_id);
Lineup.findById(lineup_id, function (err, lineup) {
if (err) {
return next(err);
}
if (!lineup) {
console.log('no lineup matched');
return next(new Error("no lineup matched"));
}
req.lineup = lineup;
return next();
});
});
In the line commented "//should I create my own error here?"
I could used "throw new Error('xyz')", but what exactly would that do? Why is it usually better to pass the error to the callback 'next'?
Another question is - how do I get "throw new Error('xyz')" to show up in the console as well as the browser when I am in development?
In general express follows the way of passing errors rather than throwing it, for any errors in the program you can pass the error object to 'next', also an error handler needs to be defined so that all the errors passed to 'next' can be handled properly.
http://expressjs.com/en/guide/error-handling.html
Throwing an error inside a callback doesn't work:
app.get('/', function (req, res) {
fs.mkdir('.', (err) => {
if (err) throw err;
});
});
But calling next works:
app.get('/', function (req, res, next) {
fs.mkdir('.', (err) => {
if (err) next(err);
});
});
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it. For example:
app.get('/', function (req, res) {
throw new Error('BROKEN') // Express will catch this on its own.
})
For those who prefer throwing errors, here is a workaround decorator:
export function safeThrow(
target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<(req: Request, res: Response, next: NextFunction) => Promise<any>>) {
const fun = descriptor.value;
descriptor.value = async function () {
try {
await fun.apply(this, arguments);
} catch (err) {
arguments[2](err);
}
};
}
#safeThrow
private async get(req: Request, res: Response, next: NextFunction) {
throw { status: 404, message: 'Not supported' }
}