Search code examples
javascriptnode.jsmongoosemongoose-schema

Mongoose post hook does not returns response in Postman when saving with await


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?


Solution

  • 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.