Please suggest better way to write this code without making callback hell - node.js

router.get('/:id',ensureLogin,(req,res)=>{
somemodel.findOne({...}) ****//mongoose call 1****
.then(result=>{
if(some condition){
User.findOne(some condition) ***//mongoose call 2***
.then(result=>{
//Some code is to be executed here using the "User.findOne..."
}
res.render('some template')
.catch(err=>{
console.log(err)
})
}
else{
res.render('some another template');
}
})
.catch(err=>{
res.render('error/500');
})
})
Here mongoose call 2 depends on mongoose call 1 for some reason. Though the code is working yet I want it to be framed properly. So please suggest a better way for doing this. I hope I cleared enough the query.
Thanks.
PS: I'm a newbie in NODE.js

The asynchronous nature of a lot of Node.js calls can be very confusing at first, but you get used to it quite quickly and realize that it's a great technology.
I'd suggest using the async / await syntax, this makes the code much more like synchronous code. It certainly makes it a lot easier to follow.
I'd refactor the code to something like this:
router.get('/:id', ensureLogin, async (req,res) => {
try {
let result = await somemodel.findOne({/* search criteria */});
if (someCondition(result)) {
let user = await User.findOne({/* search criteria */})
res.render('some template');
} else {
res.render('some another template');
}
} catch (error) {
res.render('error/500');
}
})

You would benefit from using async/await, particularly with conditionals involving different asynchronous execution branches which you have. It also makes it a bit easier to have centralized error handling for the different asynchronous code paths:
router.get('/:id', ensureLogin, async (req,res) => {
try {
let result = await somemodel.findOne({...});
if(some condition){
let secondResult = await User.findOne(some condition);
//Some code is to be executed here using the "User.findOne..."
res.render('some template')
} else {
res.render('some another template');
}
} catch(e) {
res.render('error/500');
}
}

Related

Best way to structure async Express GET request

I have been looking to update my Express skills by incorporating async/await handling and had a quick question.
From the examples I have seen online, most requests are structured wrapped inside of a try/catch block and add any await tasks to a variable before handling them.
app.post('/signup', async(req, res) => {
const { email, firstName } = req.body
const user = new User({ email, firstName })
const ret = await user.save()
res.json(ret)
})
My code looks like this:
app.route("/articles")
// GET: articles
.get(async (req, res) => {
await Article.find((err, results) => {
if (!err) {
res.json(results);
} else {
res.send(err);
};
});
})
Should I assign the response from my Mongoose to find to a variable as the first code block example and handle in a try/catch, or does my code essentially do the same thing and in a way that's the best practice as is?
Thanks in advance!
Cheers,
James
Be aware that there are two very different uses of the word async in javascript. The first is the general concept of asynchronous functions which can be implemented in any language and is widely used in javascript with various design patterns (callback, promises, async/await). The second is the async keyword which is used to allow the usage of await and only works on promises (does not work in callbacks).
You seem to be confusing the two. Because of this I now advise people not to use the word "async" when referring to asynchronous functions and only use it to refer to the async keyword.
Your function does not return a promise because you passed a callback to it. As such it cannot be used with await. Because you cannot use await it makes no sense to mark the function as async. I consider your code buggy even though it works - the mechanism still function (the await keyword conveniently ignores non-Promise functions like yours and does not generate an error) however it doesn't communicate your intent well and will confuse future maintainers of your code.
IMHO, the correct version of your code should be:
app.route("/articles")
// GET: articles
.get((req, res) => {
Article.find((err, results) => {
if (!err) {
res.json(results);
} else {
res.send(err);
};
});
})
The correct version of your code with async/await is:
app.route("/articles")
// GET: articles
.get(async (req, res) => {
try {
res.json(await Article.find());
}
catch (err) {
res.send(err);
}
})
The correct version of your code with promises but without async/await should be:
app.route("/articles")
// GET: articles
.get((req, res) => {
Article.find()
.then(result => res.json(result))
.catch(err => res.send(err));
})
The above is of course just my opinion but I suggest you strongly consider it a guideline. Any of the three forms above would be perfectly acceptable to most javascript programmers and most people consider which to use a matter of taste. Personally I prefer plain promises without await but async/await is useful when you have some tricky flow control logic.
Note that Mongoose conveniently supports both promises and callbacks so in this specific case you can just remove the callback to use async/await. However not all libraries do this. If you need to convert a callback based function to a promise you need to wrap it in the Promise constructor:
function convertedToPromise () {
return new Promise((resolve,reject) => {
callbackBasedFunction((err,result) => {
if (err) {
reject(err)
}
else {
resolve(result)
}
});
});
}

Mongoose async/ await correct method

I'm new to mongoose and node could anyone please point out which is the right method, my intention is to send the list of all products in my MongoDB to client.
method 1
items = await Product.findById({})
.then((items) => {
if (items == null) {
res.status(404).send("no items");
} else {
res.send(items);
next();
}
})
.catch((err) => {
res.status(404).send({ error: err });
});
method 2
items = await Product.find({}).exec();
res.status(200).send(items)
next();
Also could you point out how can i use again one more await call if i have to process items and send after that.Thanks in advance.
Method 2 should looks like that:
router.get('/:param', async function(req, res) {
try {
let items = await Product.find({}).lean()
res.status(200).json(items);
} catch (e) {
res.status(404).json(e);
}
});
You don't need .exec()
You need to use .lean() if you want to receive js object in items, not mongoose's.
Try not to request all docs collection at once, especially if collection have more then 1000+ documents. Otherwise do it via .cursor or limit(Number) to avoid problems with DB.
Also as already mentioned above, try to use async/await and ES6 syntax. It's just looks better.
And please, mark question as answered, just to avoid your question to be flagged.

Node.js Promise.all when function returns nothing

How to handle multiple calls to the same function when its returning nothing. I need to wait untill all calls are finished so i can call another function.
For now I'm using Promise.all() but it doesn't seem right:
Promise.all(table_statements.map(i => insertValues(i)))
.then(function(result) {
readNodeData(session, nodes);
})
.catch(function() {
console.log(err);
})
function insertValues(statement) {
return new Promise((res, rej) => {
database.query(statement, function (err, result) {
if (err) {
rej(err)
}
else{
console.log("Daten in Tabelle geschrieben")
res(); // basically returning nothing
}
});
});
}
This writes data to a database in multiple statements, i need to wait untill all are finished.
Is this actually the "right" way to do it? I mean... it works, but i have the feeling it's not how you are supposed to do it.
Using Promise.all for your case is a good call, since it returns a Promise, when all the promises passed as an iterable are resolved. See the docs.
However, for brevity and readability, try converting your insertValues into async-await function as follows. This tutorial would be a great place to start learning about async functions in JavaScript.
// async insertValues function - for re-usability (and perhaps easy unit testing),
// I've passed the database as an argument to the function
async function insertValues(database, statement) {
try {
await database.query(statement);
} catch (error) {
console.error(error);
}
}
// using the insertValues() function
async function updateDatabase(database) {
try {
// I am using 'await' here to get the resolved value.
// I'm not sure this is the direction you want to take.
const results = await Promise.all(
tableStatements.map(statement => insertValues(database, statement))
);
// do stuff with 'results'.. I'm just going to log them to the console
console.log(results);
} catch (error) {
console.error(error);
}
}
Here, insertValues() function doesn't return any value. Its operation on the database is entirely dependent on the query statement passed to it. I wrapped it within a try-catch block so as to catch any errors that might arise while performing the operation (s) above. More details on handling errors using try-catch can be found here.
Your promisified write to database looks ok, so we can update code from another part.
Let's rewrite it a little to use async/await and try/catch.
(async() => {
const promisifiedStatements = table_statements.map(i => insertValues(i));
try {
await Promise.all(promisifiedStatements);
readNodeData(session, nodes);
} catch(e){
console.log(e)
}
})();
I use here IIFE to use await behaviour.

ExpressJS - Refactoring nested callbacks and handling errors in app.get functions?

I am quite new to Node.js and already frustrated due to nested callbacks which make it very hard to read the code and troubleshoot for typos.
As you can see below, I have 2 associated models (Blog and Comment) and app.get method which I create Comment for a Blog post.
Model Structure:
Blog
..title (string)
..blog (string)
..comments (Referenced Comment Model)
....comment (string)
Comment
..comment(string)
Currently app.get method has 3 nested call back functions, possible errors are only console.logged yet (for a better user experience if I start to write more codes for errors function becomes real mess).
app.post('/blog/:id/comment',function(req,res){
Comment.create(req.body.comment, function(err, newComment){
if (err) {
console.log(err);
} else {
Blog.findById(req.params.id, function(err, foundBlog){
if (err){
console.log(err);
} else {
foundBlog.comments.push(newComment);
foundBlog.save(function(err, data){
if(err){
console.log(err);
} else {
res.redirect('/blog/'+req.params.id);
}
});
}
});
}
});
});
Here I would like to ask your suggestions to simplify below function and how to better handling errors.
As others have commented, promises is the way to go and async/await is generally the most elegant approach to writing promises. As an example, your code could be condensed to the below. You should read up on promises as they are an important concept for node development.
app.post('/blog/:id/comment', async function(req,res){
try{
const newComment = await Comment.create(req.body.comment);
const foundBlog = await Blog.findById(req.params.id);
foundBlog.comments.push(newComment);
await foundBlog.save();
res.redirect('/blog/'+req.params.id);
}
catch(err){
console.log(err);
}
});
Looks like you are using Mongoose, which supports promises, so you could do something like this:
app.post('/blog/:id/comment',(req,res) {
Comment.create(req.body.comment)
.then(newComment => {
return Blog.findById(req.params.id))
.then(foundBlog => {
foundBlog.comments.push(newComment)
return foundBlog.save()
})
})
.then(() => res.redirect('/blog/' + req.params.id))
.catch(err => console.log(err))
})
You could also use async-await:
app.post('/blog/:id/comment', async (req, res) {
try {
const newComment = await Comment.create(req.body.comment)
const foundBlog = await Blog.findById(req.params.id)
foundBlog.comments.push(newComment)
await foundBlog.save()
res.redirect('/blog/' + req.params.id)
}
catch(err) {
console.log(err)
}
})

ExpressJS: Why does this output [] on the first GET, and then the next GET returns the data from the previous?

I am experimenting with Express and MongoDB, and have a functional API server that can add, update, delete, and retrieve a single post. The issue I have run into is returning all of the documents from Mongo.
I have a GET route that outputs the results, except it does not behave as I imagined it would. When you run this, the first GET request to /notes returns and empty array, i.e. []
let notes =[];
app.get('/notes', (req, res) => {
async function getNotes() {
try {
await db.collection('notes').find().forEach(function (myDoc) {
notes.push(myDoc);
})
} catch(err) {
console.log(err)
}
console.log(notes);
res.send((notes));
}
getNotes();
});
On the second GET to /notes, however, the data that was pushed into notes[] is returned, and it is then overwritten by the newly pushed data.
Can anyone help me fill in the blank spot in my understanding of this? I imagine there is something that I just didn't understand along the way.
Edit***
I have experimented with this a bit, and am still running into the same issues.
let array= [];
async function getNotes() {
try {
await db.collection('notes').find().toArray(function (err, notesArray) {
array = notesArray;
})
} catch (err) {
console.log(err)
}
console.log(array);
return array;
}
app.get('/notes', (req, res) => {
getNotes();
res.send(array);
});
MongoDB's .toArray() won't both invoke a callback and return a Promise.
Returns:
Promise if no callback passed
And, await depends on the Promise, so you won't be able to use the callback as well.
Though, one isn't really necessary. When the promise resolves, await will return the array of documents, allowing you to assign that to array or any other variable.
try {
array = await db.collection('notes').find().toArray();
}

Resources