Reputation: 432
The error is
Cast to ObjectId failed for value "hist" at path "_id" for model "books"
Hi there!
PROBLEM:
I am trying to use other models inside of another model two models inside of one to be exact. The models are as follows
models: {
'Professor'
'Book'
}
and I would like to use the schemas inside of another mongoose model that's called checkoutHistory.
It is my understanding that in order to do this I need to do
{type: mongoose.Schema.Types.ObjectID, ref: 'book'}
Here is the model that I want to create and retrieve data from.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const checkoutHistory = new Schema({
book: { type: mongoose.Schema.Types.ObjectId, ref: 'books',required: true },
checkoutCopiesNum: {type: Number, required: true},
profChosen: { type: mongoose.Schema.Types.ObjectId, ref: 'prof', required: true },
dueDate: {type: String, required: true}
})
/* Exporting it as a mongoose model so that it can be put into the data base */
module.exports = mongoose.model('checkoutHist', checkoutHistory)
This works fine for when I am creating a doc with this model. Like so...
const checkoutHistoryMiddle = (req, res, next) => {
try {
//get the body of the request
const body = req.body
//check for data
if(!body){
return res.status(400).json({
success: false,
error: 'no body given'
})
}
const history = new CheckoutHist(body)
history.save().then(() => next()).catch(error => {
return res.status(400).json({
success: false,
message: error,
msg: "checkout save failed"
})
})
} catch (error) {
res.status(400).json({
success: false,
message: error,
msg: "checkoutHist failed"
})
}
}
So the code above will save a new history object to the database, this currently works fine.
Here's where we get into the actual issue. I cannot retrieve these documents from the mongo database because of the above error. I have the routes set up correctly:
router.get('/book/hist', BookCtrl.getCheckoutHistory)
getCheckoutHistory = async (req, res) => {
try {
return res.status(200).json({success: true, msg:"success"})
} catch (err) {
return res.status(400).json({success: false, msg:"failed"})
}
// await CheckHist.find({}, (err, hist) => {
// if (err) {
// return res.status(400).json({ success: false, error: err })
// }
// if (!hist) {
// return res
// .status(404)
// .json({ success: false, error: `History not found` })
// }
// return res.status(200).json({ success: true, data: hist })
// }).catch(err => console.log(err))
}
I commented out the code to show what I was trying to do, and to emphasize that no matter what is done in this function it does not run.
Now whats especially weird about the error is it points me to a part in the file that has nothing to do with what I am doing here is the error:
at getBookById (/Users/devintripp/Desktop/unt-library-system/server/controllers/book-ctrl.js:376:13)
Whats weird about this, is I am not actually calling this function anywhere when I access the /book/hist endpoint. Here is what this function is doing:
getBookById = async (req, res) => {
await Book.findOne({ _id: req.params.id }, (err, book) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!book) {
return res
.status(404)
.json({ success: false, error: `Book not found` })
}
return res.status(200).json({ success: true, data: book })
}).catch(err => console.log(err))
}
Very odd. Because I am accessing the endpoint like this.
useEffect( () => {
const getHistory = async () => {
// await Axios.get('http://localhost:8174/api/book/hist').then(bookHist => {
// console.log(bookHist.data.data)
// })
api.getCheckoutHist().then(hist => {
console.log(hist)
})
}
getHistory()
}, [])
api.getCheckoutHist()
does the same thing as Axios.get
and produce the same error I left it in there to show you what endpoint I am accessing. The endpoint that accesses the findById function is located at a different string http://localhost:8174/api/book/:id
Here is the full error I am getting:
CastError: Cast to ObjectId failed for value "hist" at path "_id" for model "books"
at model.Query.exec (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/query.js:4358:21)
at model.Query.Query.catch (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/query.js:4464:15)
at getBookById (/Users/devintripp/Desktop/unt-library-system/server/controllers/book-ctrl.js:376:13)
at Layer.handle [as handle_request] (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/layer.js:95:5)
at /Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:281:22
at param (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:354:14)
at param (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:365:14)
at Function.process_params (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:410:3)
at next (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:275:10)
at Function.handle (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:174:3)
at router (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:47:12)
at Layer.handle [as handle_request] (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/Users/devintripp/Desktop/unt-library-system/server/node_modules/express/lib/router/index.js:317:13) {
messageFormat: undefined,
stringValue: '"hist"',
kind: 'ObjectId',
value: 'hist',
path: '_id',
reason: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
at new ObjectID (/Users/devintripp/Desktop/unt-library-system/server/node_modules/bson/lib/bson/objectid.js:59:11)
at castObjectId (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/cast/objectid.js:25:12)
at ObjectId.cast (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/schema/objectid.js:279:12)
at ObjectId.SchemaType.applySetters (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/schematype.js:1110:12)
at ObjectId.SchemaType._castForQuery (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/schematype.js:1545:15)
at ObjectId.SchemaType.castForQuery (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/schematype.js:1535:15)
at ObjectId.SchemaType.castForQueryWrapper (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/schematype.js:1512:20)
at cast (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/cast.js:331:32)
at model.Query.Query.cast (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/query.js:4759:12)
at model.Query.Query._castConditions (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/query.js:1841:10)
at model.Query.<anonymous> (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/query.js:2098:8)
at model.Query._wrappedThunk [as _findOne] (/Users/devintripp/Desktop/unt-library-system/server/node_modules/mongoose/lib/helpers/query/wrapThunk.js:16:8)
at /Users/devintripp/Desktop/unt-library-system/server/node_modules/kareem/index.js:370:33
at processTicksAndRejections (internal/process/task_queues.js:75:11)
}
I hope you can understand my confusion and perhaps give me some insight as to what exactly is going on here that I don't understand any help is appreciated.
Upvotes: 1
Views: 2951
Reputation: 7915
You're not showing us the other routes you define for your app besides:
router.get('/book/hist', BookCtrl.getCheckoutHistory)
But I can wager that your router.get('/book/:id', ....)
route is BEFORE router.get('/book/hist', ...)
. Right?
So when you make a reqeust to .../api/book/hist
, guess who's there to intercept it? This guy /book/:id
. And it assumes that hist
is a book id. It then calls getBookById
, which runs
await Book.findOne({ _id: req.params.id }, (err, book) => {
which tries to cast hist
to an ObjectID and cannot.
So put router.get('/book/hist', BookCtrl.getCheckoutHistory)
BEFORE router.get('/book/:id', ....)
There really isn't a whole lot to declaring routes in express. However, there are a few gotchas you should be aware of. This article is very brief yet very informative and you should certainly avail yourself of it.
As for your other question regarding having "two findById functions in the same route file that go to two seperate [sic] docs", I don't see any problem with it. So long as they have two separate routes defined for them. e.g:
router.get('/profs/:profid', ProfCtrl.findProfById);
router.get('/admins/:adminid', AdminCtrl.findAdminById);
it's a completely valid approach.
Upvotes: 5