My WRONG example of using Document.save() in Mongoose
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
.
- 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!