Home > OS >  Error related to MongoDB in Express routes although the get request does not act on the database
Error related to MongoDB in Express routes although the get request does not act on the database

Time:06-21

I have a problem with my routes in an Express application. This is my app.js:

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var mongoose = require('mongoose');
var methodOverride = require("method-override");

var indexRouter = require('./routes/index');
var categoriesRouter = require('./routes/categories');
var productsRouter = require('./routes/products');

var app = express();

// create default connection to the database and binds to the error event
var mongoDB = 'mongodb srv:// ...';
mongoose.connect(mongoDB, { useNewUrlParser: true , useUnifiedTopology: true});
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));

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

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(methodOverride('_method'))
app.use('/', indexRouter);
app.use('/categories', categoriesRouter);
app.use('/products', productsRouter);
app.use(express.static(path.join(__dirname, 'public')));

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

All the links in the route/index.js and route/categories.js files work as expected. I do include the route/categories.js file anyways, in case it is related to my problem.

This is route/categories.js:

var express = require('express');
var category_controller = require('../controllers/categoryControllers');
var router = express.Router();

router.get('/', category_controller.category_listing_get);
router.get('/create', category_controller.category_create_get);
router.post('/create', category_controller.category_create_post);
router.get('/:id', category_controller.category_update_get);
router.put('/:id', category_controller.category_update_post);
router.delete('/:id', category_controller.category_delete_post);

module.exports = router;

And this is routes/products.js:

var express = require('express');
var product_controller = require('../controllers/productControllers');
var router = express.Router();

router.get('/', product_controller.products_listing_get);
router.get('/subset/:id', product_controller.products_subset_get);
router.get('/subset/create', product_controller.products_create_get);
router.post('/subset/create', product_controller.products_create_post);
router.get('/subset/update/:id', product_controller.products_update_get);
router.put('/subset/update/:id', product_controller.products_update_post);
router.delete('/subset/:id', product_controller.products_delete_post);

module.exports = router;

This is the relevant part of controllers/productControllers.js

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

exports.products_create_get = function(req, res) {
    res.send("get form for creating products") 
};

This is models/products.js

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

var ProductSchema = new Schema(
  {
    prod_name: {type: String, required: true, maxLength: 50},
    prod_description: {type: String, required: true, maxLength: 120},
    prod_category: {type: Schema.Types.ObjectId, ref: 'Category'},
    prod_price: {type: String},
    prod_stock: {type: String},
  }
);

module.exports = mongoose.model('Product', ProductSchema);

Now, if I type on the browser address bar:

localhost:3000/products/subset/

I do get the expected result. However, if I type:

localhost:3000/products/subset/create

I do get an error message on the terminal which seems to be related to the mongo database, but I do not understand this as I am not acting on the database when requesting localhost:3000/products/subset/create.

This is the error message I get:


GET /products/subset/create - - ms - - create CastError: Cast to ObjectId failed for value "create" (type string) at path "prod_category" for model "Product" at model.Query.exec (/home/paul/proj/node_modules/mongoose/lib/query.js:4777:21) at model.Query.Query.then (/home/paul/proj/node_modules/mongoose/lib/query.js:4876:15) at exports.products_subset_get (/home/paul/proj/controllers/productControllers.js:13:14) at Layer.handle [as handle_request] (/home/paul/proj/node_modules/express/lib/router/layer.js:95:5) at next (/home/paul/proj/node_modules/express/lib/router/route.js:137:13) at Route.dispatch (/home/paul/proj/node_modules/express/lib/router/route.js:112:3) at Layer.handle [as handle_request] (/home/paul/proj/node_modules/express/lib/router/layer.js:95:5) at /home/paul/proj/node_modules/express/lib/router/index.js:281:22 at param (/home/paul/proj/node_modules/express/lib/router/index.js:354:14) at param (/home/paul/proj/node_modules/express/lib/router/index.js:365:14) { messageFormat: undefined, stringValue: '"create"', kind: 'ObjectId', value: 'create', path: 'prod_category', reason: BSONTypeError: Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer at new BSONTypeError (/home/paul/proj/node_modules/bson/lib/error.js:41:28) at new ObjectId (/home/paul/proj/node_modules/bson/lib/objectid.js:67:23) at castObjectId (/home/karima/paul/proj/node_modules/mongoose/lib/cast/objectid.js:25:12) at ObjectId.cast (/home/paul/proj/node_modules/mongoose/lib/schema/objectid.js:246:12) at ObjectId.SchemaType.applySetters (/home/paul/proj/node_modules/mongoose/lib/schematype.js:1189:12) at ObjectId.SchemaType._castForQuery (/home/paul/proj/node_modules/mongoose/lib/schematype.js:1623:15) at ObjectId.SchemaType.castForQuery (/home/paul/proj/node_modules/mongoose/lib/schematype.js:1613:15) at ObjectId.SchemaType.castForQueryWrapper (/home/paul/proj/node_modules/mongoose/lib/schematype.js:1590:20) at cast (/home/kpaul/proj/node_modules/mongoose/lib/cast.js:344:32) at model.Query.Query.cast (/home/paul/proj/node_modules/mongoose/lib/query.js:5199:12), valueType: 'string'


I have tried changing the order of the statements in the app.js file, clearing the cache, changing the name of the routes, restarting my virtual machine and I just do not understand what is happening because I just want to send a short string to the browser and am not touching the database with my request and still seem to get an error related to the database.

Note: localhost:3000/categories/create also works fine.

CodePudding user response:

The order in which you declared these two routes is causing the problem:

router.get('/subset/:id', product_controller.products_subset_get);
router.get('/subset/create', product_controller.products_create_get);

Express will not match a request to the best route (whatever that may be), but the first route.

In this case, because /subset/:id is declared before /subset/create, making a request for /subset/create will match the first route.

The easiest solution is to switch their declaration so the more specific route will match first:

router.get('/subset/create', product_controller.products_create_get);
router.get('/subset/:id', product_controller.products_subset_get);

Or, if you're trying to use REST semantics, use a POST request to /subset for creating new entities.

And if :id will have a fixed format, you can limit the match to only requests that have a valid-looking id in their URL. For instance an ObjectId:

app.get('/subset/:id([a-fA-F0-9]{24})', …)

FWIW, the stack trace is actually pointing out that products_subset_get was being called:

… at exports.products_subset_get (/home/paul/proj/controllers/productControllers.js:13:14)
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  • Related