Search code examples
node.jsmongodbexpressmongoose

Patch Request Method in Node.js and Mongoose


For update I use the following code that works:

router.put('/:id', async (req, res) => {
  const { error } = validateProduct(req.body); 
  if (error) return res.status(400).send(error.details[0].message);

  const product = await Product.findByIdAndUpdate(req.params.id, 
    { 
     name: req.body.name,
     description: req.body.description,
     category: req.body.category,
     tags: req.body.tags,
     withdrawn: req.body.withdrawn,
     extraData: {
       brand: req.body.extraData.brand,
       quantity: req.body.extraData.quantity,
       type: req.body.extraData.type
     } 
   }, 
   {new: true}
  );

  if (!product) return res.status(404).send('The product with the given ID was not found.');

  res.send(product);
});

What I want to do is to create a Patch operation that updates only certain fields and not all of them as the update above. These fields are not standard but they are one of the above fields of the update operation.


Solution

  • You can try this snippet (didn't test it locally though). The idea is to only update those fields in Product, which were mentioned in req.body. Be sure your validator is secure, otherwise you can end up with nosql injection.

    router.put('/:id', async (req, res) => {
      const { error } = validateProduct(req.body); 
      if (error) return res.status(400).send(error.details[0].message);
    
      const product = await Product.findById(req.params.id).exec();
      if (!product) return res.status(404).send('The product with the given ID was not found.');
    
      let query = {$set: {}};
      for (let key in req.body) {
        if (product[key] && product[key] !== req.body[key]) // if the field we have in req.body exists, we're gonna update it
           query.$set[key] = req.body[key];
    
      const updatedProduct = await Product.updateOne({_id: req.params.id}, query}).exec();
    
      res.send(product);
    });
    

    Also I'm sure you can leverage lodash in the line, where I you use a for-in loop ;)

    The downside of this approach is that it takes 2 queries to mongo, because you need a real document to compare the thing. Also update operation doesn't trigger post save hooks of your model. If you need them - you should findById() first, update necessary fields and then hit .save() on the very document you found. Hope it helps