Mongoose Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters - mongodb

I am creating a user using Mongoose, and I want to use my own _id. I thought that you could convert any string to an ObjectId with the following code: mongoose.Types.ObjectId('4fhTTRkUYNPBUSiYIhz8YHZ9wQ02'). This is what several other Stack Overflow answers have suggested. However, when I use this code in Node, I get the following error: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters.
What am I doing wrong?
router.post("/create_user", (req, res) => {
const id = req.body.id;
console.log("/create_user id : ", id); // <-- logging successfully '4fhTTRkUYNPBUSiYIhz8YHZ9wQ02'
User.create({
_id: mongoose.Types.ObjectId(id), // <-- generating error
name: "Joe"
})
.then((u) => {
return res.json({ user: u });
})
.catch((err) => {
console.log("/create_user error : ", err);
return res.json({ error: err });
});
});

I thought that you could convert any string to an ObjectId
This is not the case, only some strings are valid ObjectIds.

Related

Mongoose: Defining 404 status for not finding a document doesnt work

I,m learning MongoDB and mongoose and now I have a problem in defining a 404 status for my route handler. Here is the code:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id
try {
const user = await User.findById(_id)
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
Now if I give it an id that doesn't exist, it doesn't give me 404 Not Found status. it only executes the catch block which is not what I want.
I would appreciate it if you tell me where I made mistake or tell me a way to get error handling for that.
Thanks
The problem
As you can see in the log
CastError: Cast to ObjectId failed for value "6082d50a2c89db3164" at path "_id" for model "User"
It means : the value you provide to findById function ("6082d50a2c89db3164") is not a valid ObjectId.Then the catch block is executed.
Suggestion
1. Validate the parameter before query in database
I understand that you're trying to provide some id that doesn't exist in the database to test. But IMHO, there a difference between 2 cases :
you provide a valid id, and this id cannot be found in the database. It should return 404 in this case
you provide an invalid id in the request, it could be a string like "6082d50a2c89db3164", or even "#Q*&$(##*" or anything we could imagine. For this case, it could be better if we validate the input (req.params._id) to ensure that the format is valid. The code will be something like this:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id;
// validate params
if(!isValidateObjectId(_id)) { // the function we need to write
res.status(200).send("Invalid params"); // you can define your status and message
return;
}
// good params, get user from database
try {
const user = await User.findById(_id)
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
2. Use findOne() method instead of findById
If you want a simpler solution, don't use findById because the function expects a valid ObjectId. We can use findOne() method :
app.get('/users/:id', async (req, res) => {
const _id = req.params.id
try {
const user = await User.findOne({_id : _id})
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
(IMHO, the first solution is better though..)
Some helpful link :
https://docs.mongodb.com/manual/reference/method/ObjectId/
Can I determine if a string is a MongoDB ObjectID?
https://mongoosejs.com/docs/api.html#model_Model.findOne

Argument passed in must be a single String of 12 bytes or a string of 24 hex characters, Mongoose ObjectId err

I actually searched a ton and I saw a ton of mentions of my problem here but none of the things I tried helped me fix the issue i'm having.
I have a Room Scheme that looks like this:
const ObjectId = mongoose.Schema.ObjectId;
const roomSchema = mongoose.Schema({
users: [{
type: ObjectId,
ref: 'User'
}],
messages: [{
type: ObjectId,
ref: 'Message',
}],
post: {
type: ObjectId,
ref: 'Post'
}
});
As you can see I have an array of users with ref to another schema Users
I'm trying to query all the Rooms that has a User ObjectId in it (search ObjectId in an array).
while I can easily get this with querying mongo from cmd using this:
db.users.find({users:ObjectId('THE_OBJECT_ID')});
when I try to get the same while using mongoose it fails with:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
Here is how my route and find looks like:
app.route('/rooms/list/:user_id')
.get((req, res) => {
var query = { users: "USER_ID" };
Room.find(query ).populate('messages').then((data) => {
res.status(200).json(data);
}).catch((err) => {
console.log(err);
});
})
I tried to create type of object ID and use it but it still doesn't work.
var mongoose = require('mongoose'),
userId = 'THE_USER_ID';
var id = mongoose.Types.ObjectId(userId);
and than
Rooms.find({'users': id });
but it still doesn't work.
I also tried altering my query search using $in, $elemmatch it worked on cmd but failed when querying using mongoose.
Any help would be appreciated.
Issue :
If you check this :
var query = { users: "USER_ID" };
(Or)
userId = 'THE_USER_ID';
var id = mongoose.Types.ObjectId(userId);
What are you trying to do here ? You are passing in string USER_ID or THE_USER_ID as input and trying to convert it to type of ObjectId(). But string inside ObjectId() has certain restrictions which is why mongoose is failing to convert passed in string value to ObjectId() and getting error'd out.
Try this code :
Code :
const mongoose = require('mongoose');
app.route('/rooms/list/:user_id')
.get((req, res) => {
var query = { users: mongoose.Types.ObjectId(req.params.user_id) };
Room.find(query).populate('messages').then((data) => {
res.status(200).json(data);
}).catch((err) => {
console.log(err);
});
})
Your input should be value of user_id (Which will be string) - Convert it to ObjectId() and then query DB. So value of user_id should be a string that obeys ObjectId()'s restrictions, You can take string from one of existing doc's ObjectId() & test your get api.

How to fix this update method in my Express Server to account for an array in a Mongoose Schema [duplicate]

This question already has answers here:
Push items into mongo array via mongoose
(11 answers)
Closed 3 years ago.
I am building a REST Api with express, mongodb and mongoose. As a part of my schema I have stated that I want an array to hold a list of values. I am unable to use postman to add or update the values in the array. I am basically wondering why this isn't working and what would be the best practice for this type of situation.
Here is my mongoose Schema
const Schema = mongoose.Schema;
let Quote = new Schema({
name: {
type: String,
},
quote: [String],
date_submitted: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('Quote', Quote);
As Stated I have an Array setup to hold multiple quotes.
Here is the method for adding a quote to the mongoDB:
let quote = new Quotes({
name: req.body.name,
quote: req.body,
});
quote
.save()
.then(quote => {
res.status(200).send('adding new quote successful');
})
.catch(err => {
res.status(400).send('adding new quote failed' + err);
});
});
This does add a record to the DB but I am not sure if this method is best practice.
This is the update method that is NOT working at all
router.post('/update/:id', (req, res) => {
// Quotes.findOneAndUpdate({ name: req.body.name }, { $push: { quote: req.body.quote } });
Quotes.findById(req.params.id, function(err, quote) {
if (!quote) {
res.status(404).send('Data is not found');
} else {
quote.author = req.body.name;
quote.quotes = { $push: { quote: req.body.quote } };
quote.date_submitted = req.body.date_submitted;
quote
.save()
.then(quote => {
res.json('quote updated');
})
.catch(err => {
res.status(400).send('Update not possible' + ' ' + err);
});
}
});
});
You can see that I tried to do the findOneAndUpdate method but it did not push the values into the array like it should. When I submit my POST request from Postman I do not get an error but when I check the Database the Array value remains the same. Any help on this would be amazing!
You could try pushing the quote to the quote.quote using a simple array#push , then saving the document.
quote.quote.push(req.body.quote)

Querying with Mongoose by a field

I'm trying to query on this mongoose object 'Order' in express. However, when I add in the querystring variable between the parentheses on find, it doesn't work. I'm a bit lost on the documentation on how to resolve this issue.
router.get('/allorders', jwtAuth, function(req,res,next) {
const userID = req.user.username;
const querystring = "{orderedByUser: '" + userID + "'}";
//Order.find() works
Order.find(querystring)
.then(orders =>{
res.json(orders).end();
}).catch( err=> {
res.status(500).json({error: 'Something went wrong!'});
});
});
You know why, because the condition you need to pass to the Model.find() must be an object:
Model.find()
Parameters
conditions «Object»
[projection] «Object|String» optional fields to return, see Query.prototype.select()
[options] «Object» optional see Query.prototype.setOptions()
[callback] «Function»
Returns
«Query»
Finds documents
For example:
// named john and at least 18
MyModel.find({ name: 'john', age: { $gte: 18 }});
// executes immediately, passing results to callback
MyModel.find({ name: 'john', age: { $gte: 18 }}, function (err, docs) {});
In your codes, you're passing a string to that method instead of an object.
Then, try this:
router.get('/allorders', jwtAuth, function(req,res,next) {
const userID = req.user.username;
const conditionsObject = {orderedByUser: userID}; // fixed
//Order.find() works
Order.find(conditionsObject) // fixed
.then(orders =>{
res.json(orders).end();
}).catch( err=> {
res.status(500).json({error: 'Something went wrong!'});
});
});
For more information about Model.find() method, you could read it here: https://mongoosejs.com/docs/api.html#model_Model.find
Hopefully it helps.
find accepts a query object, not a string. So it should be:
Order.find({orderedByUser: userID}).then(...

Why is JQuery casting a string to _id for Mongodb in this? (Please read EDIT)

I have a route that adds an image (a meme) like this:
// add new image by URL
app.post('/api/addMeme', function (req, res) {
var meme = new Meme({
title: req.body.title.trim().toLowerCase(),
image: req.body.image,
meta: {
votes: 0,
favs: 0
},
related: []
});
// Save meme to database
meme.save(function (err) {
if (err) throw err;
Meme.find({}, function (err, meme) {
if (err) throw err;
io.emit('new meme', meme);
});
res.send('Succesfully inserted meme.');
});
});
It takes the only two attribute title and image given by client side ajax and add it to my Mongodb database named Meme. Emit the updated database using socket.io. Both title and image are String type. image is suppose to be an URL to an image.
Now, I'm not ashamed to admit it, but my friend trolled my site and sent image = "www.pornhub.com" to this route and it crashed my database/site. Whenever I go and try to retrieve the image by its _id, I get the error:
CastError: Cast to ObjectId failed for value "www.pornhub.com" at path "_id" for model "meme"
EDIT: it looks like the error is actually coming from the route
app.post('/api/vote', function(req, res){
Meme.findOneAndUpdate({_id: req.body.id}, {$inc : {'meta.votes' : 1}}, {new: true}, function (err, meme) {
if (err) throw err;
if (!meme) return res.send('No meme found with that ID.');
io.emit('new vote', meme);
res.send('Succesfully voted meme.');
});
});
where a POST request is updating the database, and there's a cast error where the _id is given as a string?
The client side script that's doing this is
$("#vote").click(function(){
$.ajax({
type: "POST",
url: '/api/vote',
data: {
id: App.meme._id
},
success: function (data, status) {
console.log(data);
}
});
return false;
});
where App is a Express-state exposed data for which meme, the database, lives under.
But this error ONLY occurs on the object with image = "www.pornhub.com". My guess is that somewhere in the HTML, a cross-site href is visiting www.pornhub.com and somehow App is getting distorted? It doesn't fully make sense why id: App.meme._id would give www.pornhub.com as its value.