My WRONG example of using Document.save() in Mongoose

Image for post
Image for post

I recently have been learning the MERN stack(MongoDB, Express, React, Node.js). When I tried to combine MongoDB with my Node.js application, I came across a problem that I really want to share so that you can avoid it!

I use Mongoose to connect between the application and MongoDB. Mongoose is an ODM(Object Document Mapper) that translates between them and offers a lot of useful methods to manipulate data in the Node.js app for CRUD(Create, Read, Update, Delete) purpose.

Problem

const addUser = (user) =>{
let newUser = new User(user)
if (newUser.save()){
console.log(newUser) // it will console.log the newly created user, but it is not in the database
mongoose.disconnect()
}else{
console.log(newUser.errors)
}
}

As you can see, on the comments, what it behaved not as expected was that it would console.log the newUser object, which means newUser.save() returned true, however, when I search the database, I don’t see the newly created object. What happened? Was the newUser saved in the database or not?

Reason

I had been using Rails and thus the ActiveRecord as my ORM(Object Relational Mapper). In ActiveRecord, it has a similar save method and it will return a boolean value: true or false .

However, it is different in Mongoose. According to the document, it actually RETURNS A PROMISE. That’s why, when evaluating if (newUser.save()) , what is inside the brackets is actually a pending promise, which is considered to be true . Then things in that if block will be executed.

But at the same time, Node.js is asynchronous, so it will keep executing the codes and quits before the promise is resolved, so newUser.save() doesn’t save the object successfully.

Solution

  1. await/async
const addUser = async (user) =>{
let newUser = new User(user)
try{
result = await newUser.save()
console.log(result);
mongoose.disconnect();
}catch(err){
console.log(err)
}
}

Here, I use await to tell the program to wait for the newUser.save() to return the result of the promise first before it moves on. And to use await , I would need to add async to the method where await is used. And I use a try/catch block so that if the promise is rejected, the error can be caught instead of breaking the program.

2. callback

const addUser = (user) =>{    
let newUser = new User(user)
newUser.save((err, result) => {
if (err) console.log(err);
else {
console.log(result);
mongoose.disconnect();
}
}
)}

Here, it is even easier, I pass a callback to save method, which takes in two arguments, err(when the promise is rejected) and result (when the promise is resolved) and thus when the result of the promise came back, it will be fired and do whatever we assign in the block.

Recap

Thanks for reading and happy coding!

Web Developer | Ruby | Rails | SQL | Sinatra | React | Redux | HTML&CSS | JavaScript| MERN LinkedIn:https://www.linkedin.com/in/yingqi-chen/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store