I'm creating a post hook to create a slug after the a Post has been save. When I try and use Postman the check my result, the process go into limbo (no data was returned).I am using mongoose@8.0.0
.
Code for the post hook:
PostsSchema.post("save", async function (doc, next) {
try {
doc.postSlug = slugify(doc.postTitle, { lower: true });
await doc.save();
return next();
} catch (err) {
console.log("inside catch method");
return next(createError(500, err.message));
}
});
Code for the route I use:
module.exports.createPost = async (req, res, next) => {
const { postTitle, postContent, postAuthor } = req.body;
try {
// check if author exist
const authorExist = await AccountsModel.findById(postAuthor);
if (!authorExist) {
return next(createError(404, `Author ID: ${postAuthor} Not Found`));
}
// create post
console.log("route 1")
const postNew = new PostsModel({
postTitle: postTitle,
postContent: postContent,
postAuthor: postAuthor,
});
await postNew.save();
// populate data for return
let postCreated = await postNew.populate("postAuthor");
return res.status(200).json({
code: 1,
success: true,
message: "New Post Created",
data: postCreated,
});
} catch (error) {
return next(createError(500, error.message));
}
};
I did fix this by removing the await
in front of the doc.save()
:
PostsSchema.post("save", async function (doc, next) {
try {
doc.postSlug = slugify(doc.postTitle, { lower: true });
doc.save();
return next();
} catch (err) {
console.log("inside catch method");
return next(createError(500, err.message));
}
});
Also, using .then()
also return the result:
PostsSchema.post("save", async function (doc, next) {
doc.postSlug = slugify(doc.postTitle, { lower: true });
console.log(1);
doc
.save()
.then(() => {
console.log(2);
return next();
})
.catch((error) => {
return next(createError(500, err.message));
});
});
But found some other post what have await and the answers indicate that it is not the problem:
So, I am wondering is it the await that break the process or something else?
I found the problem, when I console.log(1)
within your post hook, it creates an infinite loop. Based on a comment under 1 of the post Mongoose post save hook , update the document, post hook is executed after a .save()
is performed; and inside your post hook, you perform another .save()
which call your post hook again, creating an infinite loop.
Your await doc.save()
doesn't work because it's waiting for the infinite loop to complete. That is why there is no response in postman, it never reached the return next()
section.
Your doc.save()
"works" because .save()
is asynchronous; before it could complete its process, it has already reached to the return next()
section that is why there is a value returned to postman while the code stays in an infinite loop.
Your doc.save().then()
"works" because .then()
resolves your .save()
and get to the return next()
but it invokes the post hook again, creating an infinite loop.
I recommend either change the keyword pass in the hook or change to pre hook:
PostsSchema.pre('save', function(next) {
if (this.isNew || this.isModified('PostTitle')) {
this.slug = slugify(this.PostTitle, { lower: true });
}
return next();
});
This pre hook runs before the actual save()
operation, so you don't call this hook again after saving it.