skip to Main Content

I am currently creating simple CRUD app that lets me create products and store it in a Mongo database. The app is working fine, except that it is constantly throwing an error message when I try to go to my ‘/products/show’ page (or when working with a specific product).

I don’t really know what is happening as this is my first time working with Mongoose and MongoDB. The app is working fine, which it should, but this error message is causing the program to continue loading even when everything is on the screen.

My index.js file

const express = require('express');
const app = express();
const path = require('path');
const mongoose = require('mongoose');

const Product = require('./models/product');

mongoose.connect('mongodb://localhost:27017/farmStand', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => {
        console.log("MONGO CONNECTION IS OPEN!");
    })
    .catch(err => {
        console.log("Oh no.. Mongo error");
        console.log(err);
    });

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(express.urlencoded({ extended: true }));

app.get('/products', async (req, res) => {
    const products = await Product.find({});
    res.render('products/index', { products });
});

app.get('/products/new', (req, res) => {
    res.render('products/new');
});

app.post('/products', async (req, res) => {
    const newProduct = new Product(req.body);
    await newProduct.save();
    console.log(newProduct);
    res.redirect(`/products/${newProduct._id}`);
});

// This is where the error is occuring.
app.get('/products/:id', async (req, res) => {
    try {
        const product = await Product.findById(req.params.id);
        res.render('products/show', { product });
    }
    catch (error) {
        console.log(error);
    }
});

app.listen(3000, () => {
    console.log("APP IS LISTENING ON PORT 3000");
});

My productSchema file

const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    price: {
        type: Number,
        required: true,
        min: 0
    },
    category: {
        type: String,
        lowercase: true,
        enum: ['fruit', 'vegetables', "dairy"]
    }
});

const Product = mongoose.model('Product', productSchema);

module.exports = Product;

My EJS ‘/show’ file

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title><%= product.name %></title>
    </head>
    <body>
        <h1><%= product.name %></h1>
        <ul>
            <li>Price: $<%= product.price %></li>
            <li>Category: <%= product.category %></li>
        </ul>
        <a href="/products">All products</a>
    </body>
</html>

The error message

CastError: Cast to ObjectId failed for value "null" (type string) at path "_id" for model "Product"
    at ObjectId.cast (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/schema/objectid.js:250:11)
    at SchemaType.applySetters (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/schematype.js:1220:12)
    at SchemaType.castForQuery (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/schematype.js:1632:15)
    at cast (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/cast.js:356:32)
    at Query.cast (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:4910:12)
    at Query._castConditions (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:2232:10)
    at model.Query._findOne (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:2519:8)
    at model.Query.exec (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:4429:28)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/index.js:40:25 {
  stringValue: '"null"',
  messageFormat: undefined,
  kind: 'ObjectId',
  value: 'null',
  path: '_id',
  reason: BSONError: Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer
      at new ObjectId (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/bson/lib/bson.cjs:2006:23)
      at castObjectId (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/cast/objectid.js:25:12)
      at ObjectId.cast (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/schema/objectid.js:248:12)
      at SchemaType.applySetters (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/schematype.js:1220:12)
      at SchemaType.castForQuery (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/schematype.js:1632:15)
      at cast (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/cast.js:356:32)
      at Query.cast (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:4910:12)
      at Query._castConditions (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:2232:10)
      at model.Query._findOne (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:2519:8)
      at model.Query.exec (/Users/nielskoop/Coding/UdemyCourse/MongoDB/38_mongoose-express/node_modules/mongoose/lib/query.js:4429:28),
  valueType: 'string',
  model: Model { Product }
}

Solutions I have tried

I could not really find much information on this subject online, and the information that was available was quite confusing. I’ve tried adding an if statement to check whether the given id isValid, but that just stops the program from working altogether. I also tried to convert the ID given by Mongoose into a simple string and store it in a separate variable so that I could work with it later, but that also stopped the program from working altogether.

Desired solution

I would like to find out what is causing this error and how to fix it. I have the feeling that when I go to the ‘/show’ page the code continues running and checking for a new ID value in Mongoose, which is not there. I might be completely wrong, however.

Thanks in advance!

2

Answers


  1. Chosen as BEST ANSWER

    Okay, it turns out that I was very close with my solution by putting the findByID() call in an if statement. This is my code now:

    app.get('/products/:id', async (req, res) => {
        try {
            const { id } = req.params;
            if (id.match(/^[0-9a-fA-F]{24}$/)) {
                const product = await Product.findById(id);
                res.render('products/show', { product });
            }
        }
        catch (error) {
            console.log(error);
        }
    });
    

    It does not throw the error message anymore. My question now is if this code is good like this, or if it can be improved in some way? Also, the page continues loading until eternity, and that is a thorn in my eye. Is that normal behavior, or is that not good? If it's not good, how do I fix it? 😊


  2. You absolutely don’t need to run an if (id.match...){...} statement to make your findById(id) work.

    The error message tells you that:

    Cast to ObjectId failed for value "null" (type string) at path "_id"
    

    So in this code:

    app.post('/products', async (req, res) => {
       const newProduct = new Product(req.body);
       await newProduct.save();
       console.log(newProduct);
       res.redirect(`/products/${newProduct._id}`); //< You are sending null
    });
    

    I suspect than on some occasions something is not quite right with your const newProduct and mongoose is not creating the Product, which then has a knock on effect when you try to redirect. You think you are redirecting to:

    res.redirect(`/products/${newProduct._id}`);
    //            /products/64f7728ac8718d458e441283
    

    but in actual fact you are redirecting to:

    res.redirect(`/products/${newProduct._id}`);
    //            /products/null
    

    So req.params.id is null in your:

    app.get('/products/:id', async (req, res) => {
       try {
          const product = await Product.findById(req.params.id); //< req.params.id = null
       }
       //...
       //...
    });
    

    You need a try/catch block in your app.post('/products'...) to find out when the creation of a product is failing. At the minute you just blindly redirect. Implement this change:

    app.post('/products', async (req, res, next) => {
       try{
          const newProduct = new Product(req.body);
          await newProduct.save();
          console.log(newProduct);
          res.redirect(`/products/${newProduct._id}`);
       } catch(err){
          console.log(err);
          next(err);
       }
    }); 
    

    Your code looks pretty good otherwise and it’s maybe just an issue with the data you are actually posting that is causing the Product not to create.

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