Cast Error while calling modal.countDocuments() & modal.count() in Mongoose - node.js

My Mongoose Function
Error Message: Cast to ObjectId failed for value "count" at path "_id" for model "myinfo""
exports.indexCount = function(req, res) {
MyInfo.countDocuments({}, function(err, count) {
if (err) {
res.json({
status: "error",
message: err,
});
}
console.log("Number of users:", count);
res.json({
status: "success",
message: "Count info retrieved successfully",
data: count,
});
});
};

The problem was with my route
Since i already have a route "/myinfo/:id". my mistake was i am using
"/myinfo/count" route for getting count values which is wrong.
because count is mapped as :id in my above route
i changed the "/myinfo/count" to "/myinfo/get/count" it works now.

Related

My API call creates a record even after the return statement?

this will be a simple answer, but I have this code, that validates if a record exists in the database, and It does, and I got a status 500 from the API call, but keeps creating the duplicate record in my table.
exports.createBet = async (req, res)=>{
betBody = req.body;
newBalance = 0.0;
Bet.findOne({
where: {
[Op.and]: [
{match_id: betBody.matchId},
{user_id: betBody.userId}
]
}
}).then(data=>{
if(data){
return res.status(500).send({message: "Bet already made for this match"});
}
})
.catch(err=>{
return res.status(500).send({ message: "Error creation the bet: " + err.message});
});
balnce = await User.findOne({
where:{
id: betBody.userId
}
})
.then(data=>{
if(data.balance < betBody.betAmount){
return res.status(500).send({ message: "Not enough balance to make that bet."});
}
return data.balance;
})
.catch(err=>{
return res.status(500).send({ message : "Error getting the user in the bet creation: " + err.message})
});
Bet.create({
match_id: betBody.matchId,
bet_amount: betBody.betAmount,
selected_winner: betBody.teamSelect,
user_id: betBody.userId
})
.then(data=>{
res.json(data)
})
.catch(err=>{
return res.status(500).send({ message: "Error creating the bet: " + err.message})
});
newBalance = balnce - betBody.betAmount;
User.update(
{ balance: newBalance},
{ where: {id: betBody.userId}}
)
.catch(err=>{
res.status(500).send({ message: "Error getting user: " + err.message})
});
};
Here it is the response of the api call
And here it is the duplicated records in my table
You should use the promise chain(or async/await correctly) to solve this issue, When a request reaches to createBet function every database call(without await one) is executing parallel, it creates a new record while checking for the existing one.
NOTE: Sometimes You might get a response already sent error. res.send does not stop execution it'll return the response but the remaining code will still execute.
exports.createBet = async (req, res) => {
betBody = req.body;
newBalance = 0.0;
try {
const bet = await Bet.findOne({
where: {
[Op.and]: [{ match_id: betBody.matchId }, { user_id: betBody.userId }],
},
}).catch((err) => {
throw { message: "Error creation the bet: " + err.message };
});
if (bet) {
throw { message: "Bet already made for this match" };
}
//... handle the cases like above, must use await
} catch (err) {
res.status(500).json({ message: err.message });
}
};
This problem causes in your database a saved record without all the fields trying to truncate your table and start a fresh,
I think here in your query all time finds a record(data) thats why we are facing this type of error
If it's not work try to debug your code with log your data which comes from your findOne query

How to Utilize Error and Result Data Callback Simultaneously in Mongoose Express?

I want to use error and result data in my controller, but got a little bit confused and always got this response :
{
"status": "fail",
"message": "Cast to ObjectId failed for value \"60f943dd4bef612ae873d34\" (type string) at path \"_id\" for model \"Student\""
}
But i get this response when the data is not found :
}
"status": "fail",
"message": "Cannot delete student data with id 60f943dd4bef612ae873d34. Student data was not found
}
My code as follows :
deleteOneStudent: async (req, res) => {
const { _id } = req.params;
const student = Student.findById({ _id });
await Student.exists({ _id }, async (err, data) => {
if (err) {
return res.status(500).json({
status: "fail",
message: err.message || "Some error occurred while deleting student data."
});
}
if (!data) {
return res.status(404).json({
status: "fail",
message: `Cannot delete student data with id ${_id}. Student data was not found`
});
} else { //the code below is for removing ref from Semester document when deleting Student document
await Semester.updateMany({ '_id': student.semester }, { $pull: { student: student._id } }, { new: true, useFindAndModify: false })
student.remove()
return res.status(200).json({
status: "success",
message: "Student data was deleted successfully",
});
}
})
},
if I remove the first if, everything works fine, but I want to utilize 404 , 500 , and 200 altogether, and I have no idea using .then() and .catch() either. Can anyone help me? Thanks in advance
It looks _id casting issue. You can wrap _id in mongoose.Types.ObjectId
await Student.exists({ _id:mongoose.Types.ObjectId(_id) }, async (err, data) => {
if (err) {
return res.status(500).json({
status: "fail",
message: err.message || "Some error occurred while deleting student data."
});
}
....
....
deleteOneStudent: async (req, res) => {
try{
const { _id } = req.params;
const student = Student.findById({ _id });
await Student.exists({ _id }, async (err, data) => {
if (err) {
return res.status(500).json({
status: "fail",
message: err.message || "Some error occurred while deleting student data."
});
}
if (!data) {
return res.status(404).json({
status: "fail",
message: `Cannot delete student data with id ${_id}. Student data was not found`
});
} else { //the code below is for removing ref from Semester document when deleting Student document
await Semester.updateMany({ '_id': student.semester }, { $pull: { student: student._id } }, { new: true, useFindAndModify: false })
student.remove()
return res.status(200).json({
status: "success",
message: "Student data was deleted successfully",
});
}
})
}
catch(error){
return res.status(500).json({
status: "fail",
message: error,
});
}
}

Cast to Number failed for value "NaN" at path error solved by server self

I'm a very beginner of Nodejs, not sure I can ask such an easy thing here.
When I built a node server to connect to mongodb, got this error:
Cast to Number failed for value "NaN" at path "count"
But, the error is gone in few minutes and the server is running again somehow as well as the count schema was inserted.
I can't find something what made it run again.
I just assumed for the reason that my node server couldn't get the value because the "count" schema disappeared.
But could you help me to think in better ways.
Here is the error what I had before.
First, there wasn't "count" schema when I checked datas collection in mongodb.
> db.datas.find()
{ "_id" : ObjectId("5cb39eca6e5e3971fa934a2d"), "name" : "myData", "__v" : 0 }
Second, node server console showed me the error below:
Data ERROR: save: { ValidationError: data validation failed: count: Cast to Number failed for value "NaN" at path "count"
at ValidationError.inspect (/home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/error/validation.js:59:24)
at formatValue (util.js:430:38)
at inspect (util.js:324:10)
at format (util.js:253:18)
at Console.log (console.js:130:21)
at /home/gkh/Desktop/Dev/myapp/app.js:46:30
at /home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/model.js:4675:16
at /home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/utils.js:255:11
at $__save.error (/home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/model.js:471:16)
at /home/gkh/Desktop/Dev/myapp/node_modules/kareem/index.js:246:48
at next (/home/gkh/Desktop/Dev/myapp/node_modules/kareem/index.js:167:27)
at next (/home/gkh/Desktop/Dev/myapp/node_modules/kareem/index.js:169:9)
at Kareem.execPost (/home/gkh/Desktop/Dev/myapp/node_modules/kareem/index.js:217:3)
at _handleWrapError (/home/gkh/Desktop/Dev/myapp/node_modules/kareem/index.js:245:21)
at /home/gkh/Desktop/Dev/myapp/node_modules/kareem/index.js:272:14
at _next (/home/gkh/Desktop/Dev/myapp/node_modules/kareem/index.js:94:14)
errors:
{ count:
{ CastError: Cast to Number failed for value "NaN" at path "count"
at new CastError (/home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/error/cast.js:29:11)
at model.$set (/home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/document.js:1073:7)
at model.set [as count] (/home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/helpers/document/compile.js:140:26)
at /home/gkh/Desktop/Dev/myapp/app.js:44:15
at /home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/model.js:4675:16
at /home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/query.js:4184:12
at process.nextTick (/home/gkh/Desktop/Dev/myapp/node_modules/mongoose/lib/query.js:2741:28)
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
message: 'Cast to Number failed for value "NaN" at path "count"',
name: 'CastError',
stringValue: '"NaN"',
kind: 'Number',
value: NaN,
path: 'count',
reason: [Object] } },
_message: 'data validation failed',
name: 'ValidationError' }
Here is part of an app.js file:
mongoose.connect("mongodb://localhost:27017/user-node");
var mongod = mongoose.connection;
mongod.once("open", function(){
console.log("DB connected!");
});
mongod.on("err", function(err){
console.log("DB err: ", err);
});
var dataSchema = mongoose.Schema({
name:String,
count:Number
});
var Data = mongoose.model('data', dataSchema);
Data.findOne({name:"myData"}, function(err, data){
if(err) return console.log("Data ERROR: ", err);
if(!data){
Data.create({name:"myData",count:0}, function(err, data){
if(err) return console.log("Data ERROR", err);
console.log("Counter initialized: ", data);
});
}
});
app.set("view engine", 'ejs');
app.use(express.static(path.join(__dirname + '/public')));
// callback function list
// render to firstPage.ejs page with params or non-params
app.get('/', function(req, res){
Data.findOne({name:"myData"}, function(err, data){
if(err) return console.log("Data ERROR : /path: ", err);
data.count++;
data.save(function(err){
if(err) return console.log("Data ERROR: save: ", err);
res.render('firstPage', data);
});
});
});
// set count to 0
app.get('/reset', function(req, res){
setCounter(res, 0);
});
// check arg in req.query.count
app.get('/set/count', function(req, res){
if(req.query.count) setCounter(res, req.query.count);
else getCounter(res);
});
// placeholder : num
// any arg can place in num param
app.get('/set/:num', function(req, res){
if(req.params.num) setCounter(res, req.params.num);
else getCounter(res);
});
function setCounter(res, num){
console.log("...setCounter...");
Data.findOne({name:"myData"}, function(err, data){
if(err) return console.log("Data ERROR: ", err);
data.count=num;
data.save(function(err){
if(err) return console.log("Data ERROR: ", err);
res.render('firstPage', data);
});
});
}
function getCounter(res){
console.log("...getCounter...");
Data.findOne({name:"myData"}, function(err, data){
if(err) return console.log("Data ERROR", err);
res.render('firstPage', data);
});
}
If I'm wrong or my code is wrong, please advice for me.
Thank you in advance!
Set a default value of 0 on the field before doing any operation. This error is coming because there is no initial value assigned to the fields.
Cast to Number failed for value "NaN" at path "count"
This is possible, if the document in the db doesn’t have count attribute.
const Data = mongoose.model('data', dataSchema);
Data.findOne({ name: "myData" }, function (err, data) {
if (err)
return console.log("Data ERROR: ", err);
if (!data) {
Data.create({ name: "myData", count: 0 }, function (err, data) { //correct
if (err)
console.log("Counter initialized: ", data);
return console.log("Data ERROR", err);
});
}
});
Though you have rightly created for the new records, I suspect it might be missing for few legacy records and might be having following structure
db.datas.find()
{ "_id" : ObjectId("5cb39eca6e5e3971fa934a2d"), "name" : "myData", "__v" : 0 }
So, when you make a call to localhost:3000/, the document’s count will be undefined hence when you data.count++ and save, it errors out.
You need to check for undefined and then update it.
app.get('/', function (req, res) {
Data.findOne({ name: "myData" }, function (err, data) {
if (err) {
return console.log("Data ERROR : /path: ", err);
}
if (!data.count) {
// for legacy records which doesn’t have this attribute
data.count = 0;
}
data.count++;
data.save(function (err) {
if (err) {
return console.log("Data ERROR: save: ", err);
}
res.render('firstPage', data);
});
});
});
Also, Since you are using mongoose for checking and updating a document. You can consider using findOneAndUpdate()
Finds a matching document, updates it according to the update arg, passing any options, and returns the found document (if any) to the callback. The query executes if callback is passed else a Query object is returned.
function setCounter(res, num) {
return Data.findOneAndUpdate({
name: 'myData'
}, {
count: num
}, {
new: true,
upsert: false
// if you don't want to create a new document if the record doesnt exist
}).then(doc => {
res.render('firstPage', doc);
}).catch(err => {
return console.log("Data ERROR: ", err);
});
}

RESTful API deleting invalid data

router.delete('/:id', function(req, res){
user.remove({
_id: req.params.id
}, function(err, users) {
if(err) {
res.status(404).send({
message: "Invalid User",
data: []
});
} else {
res.status(200).send({
message: "User deleted",
data: []
});
}
});
});
I tried to implement the DELETE endpoint for my RESTful API.
It successfully deletes an existing data when I test it on postman,
but when I try to delete an invalid data, it still gives me a delete message
with a response status of 200.
What mistake am I making?
That right when you delete an not exist data the code work fine .
But you can check in users values it return nMatched if working in mongodb.
and for all database they contain this type of flag (here flag means 'nMatched' field in users object , you can check by printing users object ). for mongodb they contains values in 0 or 1
Can you something like this.
function(err, users) {
if(err) {
res.status(404).send({
message: "Invalid User",
data: []
});
} else if (users.nMatched === 0) {
res.status(400).send({
message: "User not exist deleted",
data: []
});
} else {
res.status(200).send({
message: "User deleted",
data: []
});
}
});

Why mongoose findById return an error if ID is not found

Making an API with Node/Express + Mongo.
I'm writing some unit test and I observed if I try to get /profile/1 for _id=1 (I let mongo put the ID by default so I cannot have _id=1) i got this error
MongooseError: Cast to ObjectId failed for value "1" at path "_id"
I thought I will have an empty object User.
function getProfile(req, res) {
const userId = req.params.userId
User.findById(userId, "-password", (err, user) => {
if (err) {
console.log(err);
res.status(400)
res.json({
success: false,
err
})
res.end()
return
}
if (!user) {
res.status(404)
res.json({
success: false,
message: `Cannot find an User with the userId: ${userId}`
})
res.end()
return
}
res.json({
success: true,
user: user
})
res.end()
return
})
}
My test :
describe('Test /profile route', () => {
it('shouldn\'t find Joe Doe\'s profile with a wrong ID\n', (done) => {
chai.request(server)
.get(`/profile/1`)
.end((err, res) => {
expect(res).to.have.status(404)
done()
})
})
I thought I would have an error 404 (second if, and I know it's not the right code error, just a quick way for me to see where my test goes) but I got a 400 -> meaning an error is return.
I read mongoose documentation and I don't really see ho they explain the return value with the different methods.
The problem is that '1' is not a valid mongoose object id. Therefore it is trying to compare different types.
Try casting it to a object id like so:
userId = mongoose.Types.ObjectId(userId)
and then run your query
User.findById(userId, "-password", (err, user) => { .... });

Resources