skip to Main Content

For brief context, I’m creating a web app using ReactJS, Node.js + mongoDB. It’s purpose is to hold lists of meals and products, and from them you can easily create shopping lists.

I’m totally inexperienced with Node.js and MongoDB and learning along the way of creating this app, therefore I need your help to solve an issue.

productSchema in mongoose looks like this:

{
  uniqueId: {
    type: String,
    required: true,
    default: () => nanoid(),
    index: { unique: true },
  },
  productName: {
    type: String,
    required: true,
  },
}

mealSchema:

{
  uniqueId: {
    type: String,
    required: true,
    default: () => nanoid(),
    index: { unique: true },
  },
  mealName: {
    type: String,
    required: true,
    trim: true,
  },
  products: [
    {
      uniqueId: {
        type: String,
        required: true,
        default: () => nanoid(),
        index: { unique: true },
      },
      productName: { type: String, ref: productSchema },
      productAmount: Number,
    },
  ],
}

as you can see, the products array in mealSchema is pretty much the same as Product[] but having an extra property of productAmount.

The logic in frontend is that when you create a new Meal, you can add as much products as you want (either select from list of Products (which is retrieved from DB) or create new ones). Then the whole Meal is sent to backend, Meal is created in MongoDB collection and after that, BE also iterates through every product, and if it doesn’t exist in the ‘products’ collection, it is also added there.

I’m having an issue that when I’m trying to create a Meal and add a Product which already exists in the ‘products’ collection, backend returns me an error:

{
  "error": "Could not create meal",
  "message": "E11000 duplicate key error collection: test.meals index: products.uniqueId_1 dup key: { products.uniqueId: "ukmLU8nDtIXaBA7OzZD6R" }"
}

Can somebody please help me solve this?

2

Answers


  1. Chosen as BEST ANSWER

    here's the meal controller:

    const express = require('express')
    const router = express.Router()
    
    // Import models
    const Meal = require('../models/meal')
    const Product = require('../models/product')
    
    // Validation related imports
    const validateSchema = require('../middleware/validate')
    const mealValidation = require('../validation/mealValidation')
    
    // Create a new meal
    router.post('/', validateSchema(mealValidation), async (req, res) => {
      try {
        const meal = {
          _id: req.body._id,
          mealName: req.body.mealName,
          products: req.body.products,
        }
    
        await Meal.create(meal)
    
        const createdProducts = []
        const failedProducts = []
    
        for (const product of meal.products) {
          const existingProduct = await Product.findOne({
            productName: product.productName,
          }).exec()
    
          if (!existingProduct) {
            try {
              const newProduct = await Product.create(product)
              createdProducts.push(newProduct)
            } catch (error) {
              failedProducts.push({
                productName: product.productName,
                error: 'Failed to create product',
              })
            }
          }
        }
    
        if (failedProducts.length > 0) {
          res.status(207).json({
            meal,
            createdProducts,
            failedProducts,
          })
        } else {
          res.status(201).json(meal)
        }
      } catch (error) {
        res
          .status(500)
          .json({ error: 'Could not create meal', message: error.message })
      }
    })
    
    // Read all meals
    router.get('/', async (_req, res) => {
      try {
        const meals = await Meal.find()
        res.status(200).json(meals)
      } catch (error) {
        res.status(500).json({ error: 'Could not fetch meals' })
      }
    })
    
    // Read a single meal by ID
    router.get('/:id', async (req, res) => {
      try {
        const meal = await Meal.findOne({ _id: req.params._id })
        if (!meal) {
          return res.status(404).json({ error: 'Meal not found' })
        }
        res.status(200).json(meal)
      } catch (error) {
        res.status(500).json({ error: 'Could not fetch meal' })
      }
    })
    
    // Update a meal
    router.put('/:id', validateSchema(mealValidation), async (req, res) => {
      try {
        const meal = await Meal.findByIdAndUpdate(req.params._id, req.body, {
          new: true,
        })
        res.status(200).json(meal)
      } catch (error) {
        res.status(500).json({ error: 'Could not update meal' })
      }
    })
    
    // Delete a meal
    router.delete('/:id', async (req, res) => {
      try {
        await Meal.findByIdAndRemove(req.params._id)
        res.status(200).json({ message: 'Meal deleted successfully' })
      } catch (error) {
        res.status(500).json({ error: 'Could not delete meal' })
      }
    })
    
    module.exports = router
    

    and products controller:

    const express = require('express');
    const router = express.Router();
    
    // Import model
    const Product = require('../models/product');
    
    // Validation related imports
    const validateSchema = require('../middleware/validate');
    const productValidation = require('../validation/productValidation');
    
    // Create a new product
    router.post('/', validateSchema(productValidation), async (req, res) => {
      try {
        const product = {
          _id: req.body._id,
          productName: req.body.productName,
        }
    
        await Product.create(product)
        res.status(201).json(product)
      } catch (error) {
        res
          .status(500)
          .json({ error: 'Could not create product', message: error.message })
      }
    })
    
    // Read all products
    router.get('/', async (req, res) => {
      try {
        const products = await Product.find()
        res.status(200).json(products)
      } catch (error) {
        res.status(500).json({ error: 'Could not fetch products' })
      }
    })
    
    // Read a single product by ID
    router.get('/:id', async (req, res) => {
      try {
        const product = await Product.findById(req.params._id)
        if (!product) {
          return res.status(404).json({ error: 'Product not found' })
        }
        res.status(200).json(product)
      } catch (error) {
        res.status(500).json({ error: 'Could not fetch product' })
      }
    })
    
    // Update a product
    router.put('/:id', validateSchema(productValidation), async (req, res) => {
      try {
        const product = await Product.findByIdAndUpdate(req.params._id, req.body, {
          new: true,
        })
        res.status(200).json(product)
      } catch (error) {
        res.status(500).json({ error: 'Could not update product' })
      }
    })
    
    // Delete a product
    router.delete('/:id', async (req, res) => {
      try {
        await Product.findByIdAndRemove(req.params._id)
        res.status(200).json({ message: 'Product deleted successfully' })
      } catch (error) {
        res.status(500).json({ error: 'Could not delete product' })
      }
    })
    
    module.exports = router;
    enter code here
    

  2. The E11000 error is generated by the mongod server. The error means that the server is refusing to create the document because it would cause a duplicate key in an index that was explicitly created with the option ‘unique: true’

    The error message indicates that the index named ‘products.uniqueId_1’ in the ‘test.meals’ collection has the unique option, and the document being inserted contains { products.uniqueId: "ukmLU8nDtIXaBA7OzZD6R" }, but that value is already present in another document.

    Did you intend to restrict that collection so that only one document could reference the same "products.uniqueId" value? If that was not your intention, make sure the field is not tagged unique in your schema and drop the index from the mongod server

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search