Related
When sending a request to /customers/41224d776a326fb40f000001 and a document with _id 41224d776a326fb40f000001 does not exist, doc is null and I'm returning a 404:
Controller.prototype.show = function(id, res) {
this.model.findById(id, function(err, doc) {
if (err) {
throw err;
}
if (!doc) {
res.send(404);
}
return res.send(doc);
});
};
However, when _id does not match what Mongoose expects as "format" (I suppose) for example with GET /customers/foo a strange error is returned:
CastError: Cast to ObjectId failed for value "foo" at path "_id".
So what's this error?
Mongoose's findById method casts the id parameter to the type of the model's _id field so that it can properly query for the matching doc. This is an ObjectId but "foo" is not a valid ObjectId so the cast fails.
This doesn't happen with 41224d776a326fb40f000001 because that string is a valid ObjectId.
One way to resolve this is to add a check prior to your findById call to see if id is a valid ObjectId or not like so:
if (id.match(/^[0-9a-fA-F]{24}$/)) {
// Yes, it's a valid ObjectId, proceed with `findById` call.
}
Use existing functions for checking ObjectID.
var mongoose = require('mongoose');
mongoose.Types.ObjectId.isValid('your id here');
I had to move my routes on top of other routes that are catching the route parameters:
// require express and express router
const express = require("express");
const router = express.Router();
// move this `/post/like` route on top
router.put("/post/like", requireSignin, like);
// keep the route with route parameter `/:postId` below regular routes
router.get("/post/:postId", singlePost);
I have the same issue I add
_id: String .in schema then it start work
This might be a case of routes mismatch if you have two different routes like this
router.route("/order/me") //should come before the route which has been passed with params
router.route("/order/:id")
then you have to be careful putting the route that is using a param after the regular route that worked for me
Are you parsing that string as ObjectId?
Here in my application, what I do is:
ObjectId.fromString( myObjectIdString );
it happens when you pass an invalid id to mongoose. so first check it before proceeding, using mongoose isValid function
import mongoose from "mongoose";
// add this inside your route
if( !mongoose.Types.ObjectId.isValid(id) ) return false;
In my case, I had to add _id: Object into my Schema, and then everything worked fine.
As of Nov 19, 2019
You can use isValidObjectId(id) from mongoose version 5.7.12
https://mongoosejs.com/docs/api/mongoose.html#mongoose_Mongoose-isValidObjectId
if(mongoose.Types.ObjectId.isValid(userId.id)) {
User.findById(userId.id,function (err, doc) {
if(err) {
reject(err);
} else if(doc) {
resolve({success:true,data:doc});
} else {
reject({success:false,data:"no data exist for this id"})
}
});
} else {
reject({success:"false",data:"Please provide correct id"});
}
best is to check validity
You can also use ObjectId.isValid like the following :
if (!ObjectId.isValid(userId)) return Error({ status: 422 })
If above solutions do not work for you.
Check if you are sending a GET request to a POST route.
It was that simple and stupid for me.
All you have to do is change the parameter name "id" to "_id"
//Use following to check if the id is a valid ObjectId?
var valid = mongoose.Types.ObjectId.isValid(req.params.id);
if(valid)
{
//process your code here
} else {
//the id is not a valid ObjectId
}
I was faced with something similar recently and solved it by catching the error to find out if it's a Mongoose ObjectId error.
app.get("/:userId", (req, res, next) => {
try {
// query and other code here
} catch (err) {
if (err.kind === "ObjectId") {
return res.status(404).json({
errors: [
{
msg: "User not found",
status: "404",
},
],
});
}
next(err);
}
});
You could either validate every ID before using it in your queries (which I think is the best practice),
// Assuming you are using Express, this can return 404 automatically.
app.post('/resource/:id([0-9a-f]{24})', function(req, res){
const id = req.params.id;
// ...
});
... or you could monkey patch Mongoose to ignore those casting errors and instead use a string representation to carry on the query. Your query will of course not find anything, but that is probably what you want to have happened anyway.
import { SchemaType } from 'mongoose';
let patched = false;
export const queryObjectIdCastErrorHandler = {
install,
};
/**
* Monkey patches `mongoose.SchemaType.prototype.castForQueryWrapper` to catch
* ObjectId cast errors and return string instead so that the query can continue
* the execution. Since failed casts will now use a string instead of ObjectId
* your queries will not find what they are looking for and may actually find
* something else if you happen to have a document with this id using string
* representation. I think this is more or less how MySQL would behave if you
* queried a document by id and sent a string instead of a number for example.
*/
function install() {
if (patched) {
return;
}
patch();
patched = true;
}
function patch() {
// #ts-ignore using private api.
const original = SchemaType.prototype.castForQueryWrapper;
// #ts-ignore using private api.
SchemaType.prototype.castForQueryWrapper = function () {
try {
return original.apply(this, arguments);
} catch (e) {
if ((e.message as string).startsWith('Cast to ObjectId failed')) {
return arguments[0].val;
}
throw e;
}
};
}
I went with an adaptation of the #gustavohenke solution, implementing cast ObjectId in a try-catch wrapped around the original code to leverage the failure of ObjectId casting as a validation method.
Controller.prototype.show = function(id, res) {
try {
var _id = mongoose.Types.ObjectId.fromString(id);
// the original code stays the same, with _id instead of id:
this.model.findById(_id, function(err, doc) {
if (err) {
throw err;
}
if (!doc) {
res.send(404);
}
return res.send(doc);
});
} catch (err) {
res.json(404, err);
}
};
This is an old question but you can also use express-validator package to check request params
express-validator version 4 (latest):
validator = require('express-validator/check');
app.get('/show/:id', [
validator.param('id').isMongoId().trim()
], function(req, res) {
// validation result
var errors = validator.validationResult(req);
// check if there are errors
if ( !errors.isEmpty() ) {
return res.send('404');
}
// else
model.findById(req.params.id, function(err, doc) {
return res.send(doc);
});
});
express-validator version 3:
var expressValidator = require('express-validator');
app.use(expressValidator(middlewareOptions));
app.get('/show/:id', function(req, res, next) {
req.checkParams('id').isMongoId();
// validation result
req.getValidationResult().then(function(result) {
// check if there are errors
if ( !result.isEmpty() ) {
return res.send('404');
}
// else
model.findById(req.params.id, function(err, doc) {
return res.send(doc);
});
});
});
Always use mongoose.Types.ObjectId('your id')for conditions in your query it will validate the id field before running your query as a result your app will not crash.
I was having problems with this and fixed doing mongoose.ObjectId(id) without Types
ObjectId is composed of following things.
a 4-byte value representing the seconds since the Unix epoch
a 5-byte random value (Machine ID 3 bytes and Processor id 2 bytes)
a 3-byte counter, starting with a random
value.
Correct way to validate if the objectId is valid is by using static method from ObjectId class itself.
mongoose.Types.ObjectId.isValid(sample_object_id)
In my case, similar routes caused this problem.
Router.get("/:id", getUserById);
Router.get("/myBookings",getMyBookings);
In above code, whenever a get request to route "/myBookings" is made, it goes to the first route where req.params.id is equals to "myBookings" which is not a valid ObjectId.
It can be corrected by making path of both routes different.
Something like this
Router.get("/user/:id", getUserById);
Router.get("/myBookings",getMyBookings);
You are having the castError because the next route you called after the id route could not be attached to the id route.
You have to declare the id route as one last route.
The way I fix this problem is by transforming the id into a string
I like it fancy with the backtick:
`${id}`
this should fix the problem with no overhead
UPDATE OCT 2022
it would be best if you now used the :
{id: id} // if you have an id property defined
or
{_id: new ObjectId(id)} // and search for the default mongodb _id
OR you can do this
var ObjectId = require('mongoose').Types.ObjectId;
var objId = new ObjectId( (param.length < 12) ? "123456789012" : param );
as mentioned here Mongoose's find method with $or condition does not work properly
Cast string to ObjectId
import mongoose from "mongoose"; // ES6 or above
const mongoose = require('mongoose'); // ES5 or below
let userid = _id
console.log(mongoose.Types.ObjectId(userid)) //5c516fae4e6a1c1cfce18d77
Detecting and Correcting the ObjectID Error
I stumbled into this problem when trying to delete an item using mongoose and got the same error. After looking over the return string, I found there were some extra spaces inside the returned string which caused the error for me. So, I applied a few of the answers provided here to detect the erroneous id then remove the extra spaces from the string. Here is the code that worked for me to finally resolve the issue.
const mongoose = require("mongoose");
mongoose.set('useFindAndModify', false); //was set due to DeprecationWarning: Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify`
app.post("/delete", function(req, res){
let checkedItem = req.body.deleteItem;
if (!mongoose.Types.ObjectId.isValid(checkedItem)) {
checkedItem = checkedItem.replace(/\s/g, '');
}
Item.findByIdAndRemove(checkedItem, function(err) {
if (!err) {
console.log("Successfully Deleted " + checkedItem);
res.redirect("/");
}
});
});
This worked for me and I assume if other items start to appear in the return string they can be removed in a similar way.
I hope this helps.
I had the same error, but in a different situation than in the question, but maybe it will be useful to someone.
The problem was adding buckles:
Wrong:
const gamesArray = [myId];
const player = await Player.findByIdAndUpdate(req.player._id, {
gamesId: [gamesArray]
}, { new: true }
Correct:
const gamesArray = [myId];
const player = await Player.findByIdAndUpdate(req.player._id, {
gamesId: gamesArray
}, { new: true }
In my case the parameter id length was 25, So I trimmed first character of parameter id and tried. It worked.
Blockquote
const paramId = req.params.id;
if(paramId.length === 25){
const _id = paramId.substring(1, 25);
}
To change the string object to ObjectId instance fromString() method is not exist anymore. There is a new method createFromHexString().
const _id = mongoose.Types.ObjectId.fromString(id); // old method not available
const _id = mongoose.Types.ObjectId.createFromHexString(id); // new method.
could happen if you are sending less or more then 24 characters string as id
I have already seen the discussion about the following question with a similar title
mongoose 'findById' returns null with valid id
But my problem is not the database name since all my other connections with the same database in fact the queries on the same collection are working fine.
I am using mongoose 4.13.6, node js 6.11 and mongo 3.4.
It is a post request .
var query=req.body;
I am sending the search parameters as
var findFruit =
{
_id:query._id
}
When I print my findFruit I get :
_id:'5a1cf77920701c1f0aafb85e'
The controller function for this is :
Fruit.findById(findFruit._id,function(err,fruit){
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
I even tried find
Fruit.find(findFruit,function(err,fruit){
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
The collection for sure has the entry under this id .
I went through this git issue as well https://github.com/Automattic/mongoose/issues/3079
Unfortunately I cannot downgrade mongoose as it might affect multiple other working functions.
Edit :
I tried creating ObjectId like :
var mongoose = require('mongoose');
var ObjectID = require('mongodb').ObjectID;
var objectId = new ObjectID();
// Convert the object id to a hex string
var originalHex = objectId.toHexString();
// Create a new ObjectID using the createFromHexString function
var newObjectId = new ObjectID.createFromHexString(query._id);
The model file :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var FruitSchema = new Schema({
name : {type : String, unique : true},
color : {type : String}
});
module.exports = mongoose.model('Fruit', FruitSchema);
All my findById("id") calls returned null.
When looking at the collection with Compass I realized that all my _id elements were Strings. Note that the entire collection was imported.
I created a new element in Compass and the _id was created as ObjectId! and when I called findById("id") with that element's id it worked!
My conclusion is that there is obviously a bug with import. I have not found a way to convert the string _id fields to ObjectId's in the actual collection.
All my findById("id") calls returned null, when _id elements are Strings.
In the first place:
Check your mongodb database, if _id is stored as String, findById(id) can not find since it identifies ObjectId. If you've used import database by using mongoimport command and including _id in JSON:
Solution 1:
modify your JSON and for each document, change _id for instance:
_id: "5a68fde3f09ad7646ddec17e" to the following and run mongoimport again:
"_id": { "$oid": "5a68fde3f09ad7646ddec17e" }
Solution 2:
delete _id in the JSON file, drop collection and import again. Mongo will auto-create _id.
After any of solutions above, findById("id") will work.
Secondly:
Specifically in such cases where your _id elements are string, might be a better idea to use mongodb package: npm i mongodb
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function (err, db) {
if (err) throw err;
var dbo = db.db("your_db_name");
dbo.collection("fruits_collection")
.find({_id:'5a1cf77920701c1f0aafb85e'})
//.find({_id:'5a1cf77920701c1f0aafb85e'}, { projection: { _id: 0, name: 1, color: 1} }) // to select specific columns desired
.toArray(function (err, result) {
if (err) throw err;
console.log(result);
db.close();
});
});
The above simple code, assumed you manage error handling yourself, either through try-catch, or
sending 404 as status code, or redirect to error page template, depending on whether the code is embedded in the Express route handler or not.
Hope this helped .. :)
Still trying to figure out why findById didn't work for me but the following piece of code did it
var mongoose = require('mongoose');
var newObjectId=new mongoose.Types.ObjectId(query._id);
var params={
'_id':newObjectId
}
Fruit.find(params).exec(function (err,fruit) {
if( _.isNull(err) ){
var response = genRes.generateResponse(true,"found successfully");
callback(response);
}
else{
var response = genRes.generateResponse(false,"there occured some error : "+err);
callback(response);
}
})
#Shoom. Yes, worked for me, thanks. findById() expects ObjectID, not a String.
I did not have a constraint to create documents with a specific id, so I imported with no _id. The db had newly-assigned _id as ObjectID.
findById(id), (and updateOne({ _id: id }, ...), started working as expected.
I have a method to find a document in my database based on its ObjectID:
console.log('id: ' + id + ' type: ' + typeof id);
collection.findOne({'_id':new ObjectID(id)}, function(error,doc) {
if (error) {
callback(error);
} else {
callback(null, doc);
}
});
When I run it I get the following error:
/myPath/node_modules/monk/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/connection/base.js:245
throw message;
^
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
at new ObjectID (/myPath/node_modules/mongodb/node_modules/bson/lib/bson/objectid.js:38:11)
at /myPath/collectionDriver.js:134:41
This refers to the collection.findOne() line above.
The console log I have before that call outputs the id as a string of 24 hex characters:
id: "55153a8014829a865bbf700d" type: string
Before this I convert the id from an object to a string using JSON.stringify() but it appears to work successfully as shown in my console.log.
Running db.myCollection.findOne({_id : ObjectId("55153a8014829a865bbf700d")}) in Robomongo brings back the expected result.
The id that was passed in to my function was already an object ID in this case, so did not need a new ObjectID to be created from it.
When ObjectIDs are logged out to the console they appear as hex strings, rather than ObjectID("hexString"), so I thought I needed to convert it to do the find, but it was already in the format that I needed.
Try this:
var hex = /[0-9A-Fa-f]{6}/g;
id = (hex.test(id))? ObjectId(id) : id;
collection.findOne({'_id':new ObjectID(id)}, function(error,doc) {
if (error) {
callback(error);
} else {
callback(null, doc);
}
});
In my case, this worked:
var myId = JSON.parse(req.body.id);
collection.findOne({'_id': ObjectID(myId)}, function(error,doc) {
if (error) {
callback(error);
} else {
callback(null, doc);
}
});
Don't forget to include at the beginning:
var ObjectId = require('mongodb').ObjectID;
Yeah, I just spent thirty minutes baffled by this. If anyone finds themselves here, first check if you even need to convert to ObjectID in the first place. Like OP, I forgot that the hexstring is logged as just that - a hexstring.
Try out ObjectID(id) instead of new ObjectID(id)
Actually the Id you are fetching have whitespaces in it so adding trim() to your id will work
const id = (req.params.id).trim();
const result = await deleteOne({_id:new mongodb.ObjectId(id)});
while using this code keep in mind that your function should be async and mongodb should have been imported.
I was having the same issue but trimming the productId passed to ObjectId() worked for me
Earlier:
const objectId = new ObjectID(productId);
product = await db?.collection('products').find({ _id: objectId }).next();
Now:
const objectId = new ObjectID(productId.trim());
product = await db?.collection('products').find({ _id: objectId }).next();
I found the source of this,
make sure the value must not have any whitespace,
you can use
const ID = id.trim(); then use ObjectId(ID)
Thanks
Happy Coding
Argument passed in must be a string of 24 hex characters
if you are getting this error then.
just do this
use .trim() to delete any extra character usually a space is added to the id. and it becomes 25 hex charater .
so instead of _id = yourId;
use _id = yourId.trim()
collection.find({ _id: { $in: req.body.map(pd => ObjectId(pd)) } })
use map() on the res.body -- it works for me.
const ID = (req.body.id).trim();
Worked for me, actually (req.body.id) adds an extra space to the ID variable.
After I used .trim() method it worked.
I got the same error. Later I found that I have included a whitespace while sending the values to the server.
<input type="checkbox" name="checkbox" value="<%= item._id %>" onchange="this.form.submit()" />
In the value attribute I had typed as value="<%= item._id %> " . A whitespace after the ID. It caused to that error. After I removed it(value="<%= item._id %>" ), the problem solved.
The below code worked for me.
item.findOne({'_id':id} , (err)=>{
if(err){
console.log(err);
}
else{
console.log("Item Found");
}
});
I have tried all answers. But it doesn't work for me. Maybe if you are getting params with express. It's worked for me. Try this;
var MongoClient = require('mongodb').MongoClient;
const {ObjectId} = require('mongodb');
app.get('/:id', (req, res) => {
var reqId = req.params.id
var query = reqId.toString()
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("AllDB");
dbo.collection("Events").findOne({_id: ObjectId(query)}, function(err, result) {
if (err) throw err;
res.send(result)
db.close();
});
});
})
Update :
Hey, I give same error again, and I notice new solution. If your code like this:
app.get('/', (req, res) => {
res.send('TEST')
})
app.get('/:id', (req, res) => {
var reqId = req.params.id
var query = reqId.toString()
})
Could you try change your get request route. Maybe such as app.get('/findid/:id)
It worked for me. I was searching again and i found this way.
Thanks, Regards.
Sometimes clearing your local storage could be the solution to this error. From 'inspect' just go to Application>local storage and select the URL. Then press the 'clear all' sign. It worked for me.
Can I pass a variable to a query so:
db.collection('coll').find(query).toArray(function(err, docs) {
if(err) throw err;
docs.forEach(function(doc) {
var Id = parseInt(doc._id); // a returnd _id from previous query
Id--;
query = {_id : parseInt(doc._id) }; // returns null
// OR query = {_id : Id }; // error
db.collection('coll').find(query).toArray(function(err, beforeDocs) {
//if (err) throw err;
console.dir(beforeDocs);
});
});
In fact what I am trying to do is query by id to get the record that comes before the results given in the first query
Any ideas??
Is it at all possible to query mongodb with variables??
var query = { _id : var i, name : var name , etc...}
There is no way to search for the document that shows up before a match in a mongodb query.
I believe your best bet is something like this:
var prevDoc = null; // to store the previous document
var cursor = db.collection('collection').find({}); // get all docs
cursor.sort('_id', 1); // it only makes sense to grab previous if they're sorted
cursor.each(function(err, doc) {
if(err) throw err;
if(doc == null) { // if we reached the end of our cursor...
db.close(); // close the db connection
} else {
if( doc.foo === "bar" ) { // here you put your match criteria
console.dir(prevDoc); // here you print your previous document
}
}
prevDoc = doc; // store current doc so that it's available for next iteration
});
To answer your direct question ("Is it at all possible to query mongodb with variables?"), it is possible to query mongodb with a query which contains variables that already have values at the time the query is created. It is not possible, however, to pass new uninstantiated variables along for mongodb to manipulate somehow.
I have a collection "companies" with several objects. Every object has "_id" parameter. I'm trying to get this parameter from db:
app.get('/companies/:id',function(req,res){
db.collection("companies",function(err,collection){
console.log(req.params.id);
collection.findOne({_id: req.params.id},function(err, doc) {
if (doc){
console.log(doc._id);
} else {
console.log('no data for this company');
}
});
});
});
So, I request companies/4fcfd7f246e1464d05000001 (4fcfd7f246e1464d05000001 is _id-parma of a object I need) and findOne returns nothing, that' why console.log('no data for this company'); executes.
I'm absolutely sure that I have an object with _id="4fcfd7f246e1464d05000001". What I'm doing wrong? Thanks!
However, I've just noticed that id is not a typical string field. That's what mViewer shows:
"_id": {
"$oid": "4fcfd7f246e1464d05000001"
},
Seems to be strange a bit...
You need to construct the ObjectID and not pass it in as a string. Something like this should work:
var BSON = require('mongodb').BSONPure;
var obj_id = BSON.ObjectID.createFromHexString("4fcfd7f246e1464d05000001");
Then, try using that in your find/findOne.
Edit: As pointed out by Ohad in the comments (thanks Ohad!), you can also use:
new require('mongodb').ObjectID(req.params.id)
Instead of createFromHexString as outlined above.
That's because _id field in mongo isn't of string type (as your req.params.id). As suggested in other answers, you should explicitly convert it.
Try mongoskin, you could use it like node-mongodb-native driver, but with some sugar. For example:
// connect easier
var db = require('mongoskin').mongo.db('localhost:27017/testdb?auto_reconnect');
// collections
var companies = db.collection('companies');
// create object IDs
var oid = db.companies.id(req.params.id);
// some nice functions…
companies.findById();
//… and bindings
db.bind('companies', {
top10: function(callback) {
this.find({}, {limit: 10, sort: [['rating', -1]]).toArray(callback);
}
});
db.companies.top10(printTop10);
You can use findById() which will take care of the id conversion for you.
company = Company.findById(req.params.id, function(err, company) {
//////////
});
In case these didn't work for you, this worked for me for accessing a blog post:
const getSinglePost = async (req, res) => {
let id = req.params.id;
var ObjectId = require('mongodb').ObjectId;
const db = await client.db('CMS');
const data = await db.collection("posts").findOne({ _id: ObjectId(id) })
if (data) {
res.status(200).send(data)
} else res.status(400).send({ message: "no post found" })
}