Mongoose findById method not properly casting to id strings to ObjectId

Following the MDN Web Docs server side Local Library Tutorial, for the Genre Detail Page, (Genre Detail Page, the tutorial mentions a mongoose error coming from the req.params.id.

The tutorial recommends that we use mongoose.Types.ObjectId() to convert the id to a type that can be used. I implemented the mongoose.Types.ObjectId() code, but am now getting a different error: “BSONTypeError: Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer”.

Does anyone know the fix for the BSON Type Error?

Johnson

Using the following dependencies:

node v16.17.0
"async": "^3.2.4",
"express": "^4.16.1",
"mongoose": "^6.5.3",

This is what I have in my genreController.js:

const Genre = require('../models/genre');
const Book = require('../models/book');
const mongoose = require('mongoose');
const async = require('async');
........................
// Display detail page for a specific Genre.
exports.genre_detail = (req, res, next) => {
  const id = mongoose.Types.ObjectId(req.params.id);
  async.parallel(
    {
      genre(callback) {
        Genre.findById(id).exec(callback);
      },

      genre_books(callback) {
        Book.find({ genre: id }).exec(callback);
      },
    },
    (err, results) => {
      if (err) {
        return next(err);
      }
      if (results.genre == null) {
        // No results.
        const err = new Error('Genre not found');
        err.status = 404;
        return next(err);
      }
      // Successful, so render
      res.render('genre_detail', {
        title: 'Genre Detail',
        genre: results.genre,
        genre_books: results.genre_books,
      });
    }
  );
};

This is what I have in my genre.js:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const GenreSchema = new Schema({
  name: { type: String, required: true, minLength: 3, maxLength: 100 },
});

// Virtual for genre's URL
GenreSchema.virtual('url').get(() => {
  return `/catalog/genre/` + this._id;
});

//Export model & Compile model from schema
module.exports = mongoose.model('Genre', GenreSchema);

And here are my routes for genre:

// GET request for one Genre.
router.get('/genre/:id', genre_controller.genre_detail);

// GET request for list of all Genre.
router.get('/genres', genre_controller.genre_list);

Hi,

I think that mongoose.Types.ObjectId() is only required for aggregate() query.

For find() queries, Mongoose will parse the string internally. Try just pass req.params.id without mongoose.Types.ObjectId().

Also, try to console.log(req.params.id) and check what’s the output.

Hi,

Thanks for following up on this, much appreciated. I have literally just now finally resolved the issue. So, to declare the “Virtual” property in the Mongoose Schema, apparently you have to use the notation as explicitly laid out in the Mongoose docs.

I was using arrow function notation (which I generally prefer when writing JavaScript) as in the following:

/* Virtual for this genre instance URL. */
GenreSchema
.virtual('url')
.get( () => {
  return '/catalog/genre/'+this._id;
});

The Mongoose docs use the following notation, where “function” is spelled out:

/* Virtual for this genre instance URL. */
GenreSchema
.virtual('url')
.get(function () {
  return '/catalog/genre/'+this._id;
});

Apparently, the code doesn’t work with arrow notation. Lesson learned.

Thanks again for following up.

Johnson