I have some Mongoose code which fails running on NodeJS.
The relevant line is:
// get data from database
const existingUser = await User.findOne({ userId });
userId
is a "number" type.
console.log(typeof userId); // -> number
console.log(userId); // -> 1
This value is obtained from the body of a http request where the body is encoded as JSON.
Therefore I am not surprised to find that the type is "number" because all JSON numbers are the same (floating point, no integers) however I am surprised to find that this causes an error and Mongoose cannot convert the number to an integer type.
Here the request is received by a post endpoint defined in express.
app.post("/test", async (request, response) => {
// get user id from body of request
console.log(request.body); // -> { userId: 1 }
const userId = request.body.userId;
// ...
How can I fix this?
The exact error is:
CastError: Cast to ObjectId failed for value "1" (type number) at path "userId" for model "User"
reason: BSONError: input must be a 24 character hex string, 12 byte Uint8Array, or an integer
Edit: Schema, on request
const user = new mongoose.Schema({
userId: {
type: mongoose.Types.ObjectId,
unique: true,
required: true,
},
});
2
Answers
For some reason it requires an explicit cast, or creation of an
ObjectId
type:When you query a collection on a property that is of type
ObjectId
mongoose will internally cast your query parameter value to anObjectId
in order to do an equality check. This is very handy.However, a great source of confusion is that an
ObjectId
such asObjectId("6578c06c3f3559a5e969fc88")
is made up of:In your case you have a number
userId
whereMongoose sees that
User.userId
is anObjectId
and tries to cast youruserId
value to anObjectId
and can’t so it throws aCast to ObjectId failed
error.What you have done as a workaround is use the
mongoose.Types.ObjectId()
function to make a newObjectId
and then use that value to find a match. However that function not only takes a valid 24 character hexadecimal string but it also takes an integer.You think you have converted your
userId
value to anObjectId
that will match a document but all you have done is create anObjectId
that is 1 second after the Unix Epoch timestamp combined with the 5-byte random value and the 3-byte incrementing counter. To illustrate:Your value of 1 has created the
00000001
part of the newObjectId
. You can run it a million times and you will always get the same00000001
.I am fairly certain you won’t have any documents that were created at 00:00:01 UTC on 1 January 1970 so while your query won’t error it won’t find any documents.
All you need to do is allow your front-end to send a valid string form of an
ObjectId
such as{ userId : "6578c06c3f3559a5e969fc88" }
and mongoose will do the rest.