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

Yingqi Chen
3 min readMay 31, 2020

--

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

The codes I show you here is my addUser method, which will take in a user object and then create a new user and save it into Database with the help of Mongoose’s Document.save() method. Long story short, let me show you my WRONG codes:

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

Do you know why now? From the title of this article, you should know it is something related to the method Document.save() . What happens is that I have the wrong expectation of what is returned from that method.

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

So do we solve the problem? You can use two ways: await/async or callback .

  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

People like me that get used to using activerecord ORM, should pay attention to the return value of the handy methods provided by Mongoose. Besides save method, there are still a lot of methods that return a promise. The best way to address these problems is to check out the documents!

Thanks for reading and happy coding!

--

--

Yingqi Chen

Software engineer and a blockchain noob. Excited about the new world!!LinkedIn:https://www.linkedin.com/in/yingqi-chen/