Nodejs Async/Await in api controller responds with unknown error - node.js

export function getAllHost(req, res) {
function findAllHost() {
let query = {};
query.company = req.query && req.query.companyId ? req.query.companyId : res.locals.payload.companyId;
query.active = true;
if(req.query && req.query.name) {
let regexp = new RegExp('\\b' + req.query.name);
query['name'] = {$regex: regexp, $options: 'i'};
}
return Host.find(query);
}
async function sendRes() {
let allHost = [];
let hosts = [];
try {
hosts = await findAllHost();
} catch(err){
console.log(err)
}
for (let host of hosts) {
allHost.push(new Host_V1(host));
}
return allHost
}
sendRes().then(data => res.status(200).json(data)).catch(error => {
console.log("error is", error);
res.status(error.status || 500).json({
message: error.status ? error.message : 'Server Error'
});
});
}
I have been trying to adapt async/await into my code, so I converted one of the Promise based api controller to make use of async/await, but the thing that bothers me is that my server responds with 500, and the console.log inside my catch block doesn't print anything.
No error gets thrown.
I am using babel babel-plugin-syntax-async-functions to parse it.
What is it that I am doing wrong?

Your code is a bit overcomplicated, but judging by it you should receive an error in the console if one appears. It could instead be that you have a middleware producing an error? The main issue is that you're catching the error in the async function sendRes, so the .catch-method you call on the returned Promise will never be fired even if there is an error.
Like many others that are new to async/await, you've misunderstood and believe that you have to wrap every await expression in a try/catch-block. This is not the case. The error "trickles" up the call chain, and unless a particular function can provide a different return value, it's best to catch the error from the top-most callee. Take this simple example which shows a common anti-pattern: https://repl.it/repls/PunySafeInterfacestandard (await and async isn't even needed in these examples, but I added them for clarity)
But if you try to simplify your code, maybe something like the below snippet, you might be able to rule out if it's a middleware or not.
export async function getAllHosts (req, res) {
try {
let query = {
company: req.query && req.query.companyId ? req.query.companyId : res.locals.payload.companyId,
active: true
}
if (req.query && req.query.name) {
let regexp = new RegExp('\\b' + req.query.name)
query.name = {$regex: regexp, $options: 'i'}
}
let allHosts = (await Host.find(query)).map((host) => new Host_V1(host))
return res.json(allHosts)
} catch (e) {
console.log("error is", e)
res.status(error.status || 500).json({
message: error.status ? error.message : 'Server Error'
})
}
}
Take note of the (await Host.find(query)) line. If the Promise that's returned from Host.find() rejects, the .map()-method won't be executed on the data and execution will jump to the catch-block.
I also heavily discourage using Babel since async/await has been natively supported in Node since version 7.6.

EDIT : The value returned is indeed a promise
I think that the error is related to your 'SendRes.then' since what SendRes returns is not a promise but it's actually is the Array allHost
Your data argument is not an argument anymore it is the value returned from the sendRes function thanks to your async / await implementation.
const hosts = sendRes();
res.status(200).json(hosts);
if you want to handle an error you must return something from your catch block so that you can can handle it here.
if (!hosts) res.status(500);
To simplify your code you can also get rid of the sendRes function and make your getAllHost express middleware an async function
async getAllHost(req, res) { /*...*/ }
try {
let allHosts = await findAllHosts();
res.status(200).json(allHosts);
} catch (error) {
res.status(500).send({ error })
}

Related

NodeJS: promiseAll getting stuck

One of the promises is not being resolved, what can be the issue possibly?
const items = await Promise.all(data.map(async i => {
const tokenUri = await contract.tokenURI(i).catch(function (error) {return;});
if (tokenUri.length < 8) {
return;
}
let url = "http://127.0.0.1:5001/api/v0/get?arg=" + tokenUri.slice(7)
const meta = await axios.post(url).catch(function (error) {return;});
success++;
}), function (err) {
callback(err);
})
This part of the code is misplaced:
, function (err) {
callback(err);
})
Currently, it is being passed as a second argument to Promise.all(). Promise.all() only takes one argument (an iterable like an array) so that's being ignored and that callback will NEVER get called.
If you meant for that to be like the second argument to .then(), then you'd have to have a .then() to use it with, but you're trying to use it as second argument to Promise.all() and that is not correct.
If you want to know when Promise.all() is done, you would do this:
try {
const items = await Promise.all(data.map(async i => {
const tokenUri = await contract.tokenURI(i).catch(function(error) { return; });
if (tokenUri.length < 8) {
return;
}
let url = "http://127.0.0.1:5001/api/v0/get?arg=" + tokenUri.slice(7)
const meta = await axios.post(url).catch(function(error) { return; });
success++;
}));
console.log("Promise.all() is done", items);
} catch (e) {
console.log(e);
}
Note, you are silently "eating" errors with no logging of the error inside this loop which is pretty much never a good idea.
If, as you propose, one of your promises is actually never being resolved/rejected, then none of this code will do anything to fix that and you will have to go down to the core operation and find out why it's not resolving/rejecting on its own. Or, configure a timeout for it (either in the API or by manually creating your own timeout).

Try catch block doesn`t catch error in node.js

class AuthController {
static methods = {
GET: {
'/auth/signup': {
func: AuthService.signUp,
response: (data, res) => {
res.statusCode = 200;
res.end(JSON.stringify(data));
},
},
},
};
static use(req, res) {
const route = this.methods[req.method][req.url];
if (!route) {
res.statusCode = 404;
res.end(JSON.stringify({ message: 'Not found 404!' }));
return;
}
try {
const data = JSON.parse(req?.body?.data || '{}');
const result = route.func({ ...data });
route.response(result, res);
} catch (err) {
console.log(err, 'here');
res.statusCode = err.statusCode || 500;
res.end(JSON.stringify(err.message));
}
}
}
class AuthService {
static async signUp({ login, password }) {
if (!login || !password) throw new BaseError(400, 'kl', 'Custom error');
}
}
It shows the error in console but try catch block doesn't see it.
Here is the traceback.
I don't know what the reason is because the function which throws error is inside of the block. Help please!
The trace back that you attached tells you exactly what the problem is and what you need to do:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch()
You can't catch an exception thrown by an async function with a try..catch block outside of that function, because script execution reaches the catch block before the async execution is finished. You therefor have to use .catch(..) instead:
const result = route.func({ ...data }).catch((err) => {
console.log("catched error: ", err);
});
I see one issue. You have declared signUp() to be async. That means it always returns a promise and it means that any throw operations inside it reject that promise that it returns (the exception doesn't propagate synchronously). But, when you attempt to call it here:
const result = route.func({ ...data });
You don't await it so when signUp() rejects, the promise goes into result, but nobody ever handles the fact that the promise rejected and you get UnhandlePromiseRejectionWarning from the system.
I can't see the whole overall design (of all the other routes), but perhaps you just need to add await to this:
const result = await route.func({ ...data });
And, you would have to make .use() be async also.
Or, if signUp() doesn't actually need to be async, then just remove the async from its declaration and the throw will be synchronous (instead of being turned into a rejected promise) and your try/catch will catch it then.

Struggling with calling a function that uses promises in node.js

I am struggling with some code... The 2 examples below I would think would work the same but the second example throws an error? I am also struggling to figure out the error, it's not bubbling up? Admittedly I am not a seasoned node developer so any guidance would be much appreciated! If it's relevant the create method in the module is calling the sequelize create.
This works
var p1 = deliverabiltyConfigs.create2(cfgObject);
return Promise.all([p1]).then(function([res1]) {
res.json({result: res1})
});
This does not
deliverabiltyConfigs.create2(cfgObject).then(res1 =>{
res.json({result: res1})
})
Here is the function that I am calling in a controller module
exports.create2 = (dConfig) => {
DeliverabilityConfig.create(dConfig)
.then(data => {
return data
})
.catch(err => {
return {
message:
err.message || "Some error occurred while createing this config."
};
});
};
The create2 function always returns null, so neither invocation will work. Promise.all([p1]) hides the problem, returning a promise to perform an array of no promises.
create2(cfgObject).then(res1 =>{ attempts to invoke then() on null, generating a more obvious error. But neither way works.
Fix by deciding which promise syntax you want, using each as follows:
Using original promise syntax....
exports.create2 = dConfig => {
// note the return
return DeliverabilityConfig.create(dConfig)
.catch(err => {
const message = err.message || "Some error occurred while createing this config.";
return { message };
});
};
// caller
deliverabiltyConfigs.create2(cfgObject).then(result =>{
res.json(result);
})
With recent syntactic sugar...
exports.create2 = async (dConfig) => {
try {
// its fine to not await here, since the caller will await
// but just to illustrate how you might perform more async work here...
return await DeliverabilityConfig.create(dConfig);
} catch (err) {
const message = err.message || "Some error occurred while createing this config."
return { message }
}
}
// caller
var result = await deliverabiltyConfigs.create2(cfgObject);
res.json(result);
Use Promise.all() to run >1 promise concurrently. You've only got one promise in the OP, so no reason for it here.

How to put 2 logic block in same async route in nodeJs?

I have a post route which calls a service and has a .catch handler to handle any errors, also the code itself has try - catch block. But when I'm trying to call another service only of first service callback value is not desired, it shows following errors. Eg. Await cannot be used outside async, or when it goes to condition 2, app crashes. Here's the structure-
router.post('/Students', async(req,res) => {
try{
...
studentService(req.body). then ((res)=>{
....// if res.body == 'Student'
.....
res.send(res.body)
}
})
.catch(error){
....
}
catch(err){
....
}
}
module.exports = router
Now how can I check that if res.body!='Student' or the studentService fails, then go to second block which calls another service and has it's own error handler.(Note- It executes only if first one is false or desired value not obtained)
Not sure where and how to place it.
I guess asynce in your example code is a typo.
Inside an async function you can use await rather than .then().
Also, it's probably best if you don't overwrite your route's res with the output from your studentService.
And, in a route you must do something in all cases. Send a result, throw an error, send an error, whatever. The else side of your if doesn't do anything in your sample.
You can throw an error by calling express's next() with a parameter.
So try this.
const createError = require('http-errors')
...
router.post('/Students', async(req, res, next) => {
try {
...
const student = await studentService(req.body)
if (student.body === 'Student') {
...
return res.send(student.body)
}
catch (error) {
return next(error)
}
try {
const something = await someOtherService(req.body)
if (something.whatever === 'Underpaid Adjunct Faculty') {
return res.send(something.body)
}
} catch (error) {
return next(error)
}
return next(createError(400, 'Got an error'))
}
module.exports = router

Mongoose promise never gets to .then()

I am using q and I have multiple mongoose .exec() promises that never gets to the .then() part of the code, so never allow the q to resolve. Can't figure out why it never comes back.
var defer = q.defer();
var promises = [];
console.log('Exams:', exams.length);
for (var e=0; e<exams.length; e++) {
console.log('Exams:', exams[e]._id);
var newPromise = Pupilexam.find({ _exam: exams[e]._id }).populate('_user').exec()
.then((pupils) => {
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
resolve(exams[e]);
})
.catch((err) => {
reject(err);
});
console.log(typeof newPromise);
promises.push(newPromise);
console.log("Promised pushed");
}
q.all(promises).then(function(data){
console.log("q'd all");
defer.resolve(res.status(200).json(exams));
});
return defer;
The Pupilexam.find().exec() never reaches the .then() so the promises never resolve and the defer never resolves. Why would the mongoose find not get to the .then()? What have I missed?
*** UPDATE ***
Even using the built in promises, we get the same issue. The Pupilexams.find() call never comes back.
var promises = [];
for (var e=0; e<exams.length; e++) {
console.log('e:', e);
console.log('Exam', exams[e]._id);
var newPromise = Pupilexam.find({ _exam: exams[e]._id }).populate('_user').exec()
.then((pupils) => {
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
})
.catch(handleError(res));
promises.push(newPromise);
}
Promise.all(promises).then((exams) => {
console.log(values);
res.status(200).json(exams)
});
With this method I also get a headers error on the call UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
** ADDITIONAL CODE REQUESTED **
function handleError(res, statusCode) {
statusCode = statusCode || 500;
return function(err) {
console.log(err.message);
res.status(statusCode).send(err);
};
}
To answer the updated question regarding the Cannot set headers after they are sent to the client error. Looks like you send a response to the client inside your handleError function. Now, if more than one Pupilexam.find call fails, handleError would be invoked twice, resulting in the mentioned error.
You should move the catch-handler down to the Promise.all call:
const promises = [];
for (const exam of exams) {
const newPromise = Pupilexam
.find({ _exam: exam._id }).populate('_user').exec()
.then((pupils) => {
exam.pupils = pupils;
});
promises.push(newPromise);
}
Promise.all(promises)
.then((exams) => {
res.status(200).json(exams);
})
.catch(handleError(res));
I guess that you are indeed returning your promise but you are returning an empty json.
There are 2 problems with your approach:
You are not returning from your then: should return pupils and it is returning undefined
You are logging values that I don't know what it is
.then((pupils) => {
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
// you should return something // return pupils
})
promises.push(newPromise);
Promise.all(promises).then((exams) => {
// ['undefined', 'undefined', ...]
console.log(values);
res.status(200).json(exams)
});
Looks like the answer was that on these two lines the exams[e] is not in scope, because by the time the promise comes back the loop has moved on, so e is wrong and gets too high so it was erroring.
console.log("Adding pupils", exams[e]._id);
exams[e].pupils = pupils;
Only discovered that when I read #eol's message about the catch and decided to catch it properly and output.
it is Look from your code.
//(async) function
for (var e of exams) {
try {
const pupils = await Pupilexam.find({ _exam: exams[e]._id
}).populate('_user').exec().lean()
e.pupils = pupils
}catch((err){
//handleError
};
}
res.status(200).json({data: exams})
maybe that will show you how match are you wrong

Resources