mongoose throws internal server error when ID doesn't exist - node.js

mongoose findOne query throws Interal Server Error (500) when looking for a value that doesn't exists.
I'm pretty sure it should return null or empty array instead of throwing error.
Mongoose version: ^5.8.7
First attempt
router.get('/:myId', async(req, res, next) => {
const myId = req.params.myId;
await myDocument.findOne({ _id: myId }, (err, myData) => {
if (!myData) {
res.status(404).render('404');
} else {
res.render('myPage', myData);
}
}).exec()
});
second attempt
router.get('/:myId', async(req, res, next) => {
const myId = req.params.myId;
const myData = myDocument.findOne({_id: myId}).exec();
if (myData) {
//render normal page
}
// render 404 page
});
According to documentation, this should NOT happen.
Note: conditions is optional, and if conditions is null or undefined, mongoose will send an empty findOne command to MongoDB, which will return an arbitrary document. If you're querying by _id, use findById() instead.
Also tried to use findById() and find(). All cases the same happens.
It works perfectly when I pass a valid ID parameter.
How to search for data without throwing http error in case Id doesn't exists in collection?

router.get('/:myId', async(req, res, next) => {
You should check your route

You should try this code. First assign the ObjectId on top where are dependencies being called.
MongoDB id is not string. It is adviseble to pass the ObjectId.
const { ObjectId } = require('mongodb');
router.get('/:myId', async(req, res, next) => {
const myId = ObjectId(req.params.myId);
const myData = await myDocument.findOne({_id: myId}).exec();
if (myData) {
//render normal page
}
// render 404 page
});

The error was actually happening during the page render.
In fact, mongoose does NOT throws any error in this case.
I was trying to render a html page while setting ejs as View Engine.
Apparentely we can't mix things.

Related

skipping documents in mongodb for pagination

I want to skip some documents according to the page count.
For example when I make a GET request to http://localhost:3001/posts?page=2 I want to get 10 documents per page from 10-20.
router/posts.js
const express = require("express");
const {
getPosts,
createPost,
updatePost,
updateLikeCount,
getPostById,
threeLatestPosts,
deletePost,
getTenPostsPerPage,
} = require("../controllers/posts");
const verifyToken = require("../utils/verifyToken");
const router = express.Router();
router.get("/threelatest", threeLatestPosts);
router.get("/", getPosts);
router.post("/", verifyToken, createPost);
router.put("/:id", updatePost);
router.get("/:id", getPostById);
router.delete("/delete/:id", verifyToken, deletePost);
// this is how I do the GET request
router.get("/?page=:page", getTenPostsPerPage);
module.exports = router;
Here is what I have tried to skip the document but it doesn't even make the GET request and I don't even get back the console.log
const getTenPostsPerPage = async (req, res) => {
try {
console.log("this is not getting logged!")
const page = req.params.page;
const perPage = 10;
const skip = perPage * (page - 1);
const post = await PostDB.find()
.sort({ createdAt: -1 })
.skip(skip)
.limit(perPage);
if (!post) {
return res.status(404).json({ message: "Post not found" });
} else {
res.status(200).json(post);
}
} catch (err) {
res.status(400).json({ message: err });
}
};
when I make a GET request http://localhost:3001/posts?page=2 from postman, it returns all the post documents but I expect to get the documents from 10-20. The console.log is not logged in the terminal also.
I think it's because you are trying to include the querystring as part of the path.
If you see the documentation for expressjs here: http://expressjs.com/en/guide/routing.html
have a look at the section "Route paths".
You will see that
Query strings are not part of the route path.
and
The characters ?, +, *, and () are subsets of their regular expression
counterparts.
So your question mark is not being interpreted how you expect.
I imagine what is happening is that it is one of your other routes matching your request, and so that is why you are not seeing your console.log either. It's not even hitting this route.

Is there any solution for "n --- property 'topology' closes the circle" in Postman while working with nodejs and mongodb?

I'm writing the code in nodeJS for getting the user from MongoDB.When I sent the request http://localhost:5000/api/users in Postman, I ran into an error. I have seen a similar problem but all the answers are about the addObject, toObject and constructors in Javascript.I have used toObject and error is not about the "toObject" method. Here is my code,
const getUsers = (req, res, next) => {
let users;
try {
users = User.find({}, '-password');
} catch (err) {
const error = new HttpError(
'fetching user failed',
500);
return next(error);
}
res.json({users: users.map(user=> user.toObject({ getters: true}))})
};
Here is the full error message as it is not allowed to write more than 150 characters in the question box:
{
"message": "Converting circular structure to JSON\n --> starting at object with constructor 'NativeTopology'\n | property 's' -> object with constructor 'Object'\n | property 'sessionPool' -> object with constructor 'ServerSessionPool'\n --- property 'topology' closes the circle"
}
Any help would be appreciated.
it was occuring because i was not using async await. any mongoose operation like find() findOne, etc returns a promise and to get the proper data you should resolve it using .then(), .catch() or async await.
This statement was the life saver:
You must catch errors that occur in asynchronous code invoked by route handlers or middleware and pass them to Express for processing.
const getUsers = async (req, res, next) => {
let users;
try {
users = await User.find({}, '-password');
} catch (err) {
const error = new HttpError(
'Fetching users failed, please try again later.',
500
);
return next(error);
}
res.json({users: users.map(user => user.toObject({ getters: true }))});
};
It worked! After hours of searching i solved it. Also, if someone still gets stuck this is the resource http://expressjs.com/en/guide/error-handling.html where you can find answers of so many questions while working with express and nodejs.

Hi. How to use find query in mongoose - Express + MongoDB base

What I'm doing is trying to write logic for cart in my web-app what I'm creating. Problem what I'm dealing right now is that when I use query to find cart by id :
const isCart = await cartModel.findById("5f82372f2654ce1d18553ac4");
or like this
const isCart = await cartModel.find({ _id : "5f82372f2654ce1d18553ac4"} );
if cart exist with this id all works good it returns me this cart, but if in DB cart doesn't exist with this id then it throws me an error. WHY (pic below) ?? It would make more sense if it returns empty array or object so I can continue building logic of I want to do. But once it throws error it shuts down all further logic. I can't write if there is nothing found, create new cart and so on. Hopefully you got what I mean. Is it something I don't write correctly query or it is the way it is. If so, then I guess I should already control it on client side - if it returns error to a client side then client side sends new request to a new rout on creating a new cart and so on...
Here is solution what I did after someone told me that I might try do with try - catch
Here is a result, it works I'm not sure if this solution is right tho
async function sendCart (code, cart, res){
res.status(code).json({
status: "success",
data: {
cart
}
})
}
exports.createCart = async (req, res, next) => {
try{
const isCart = await cartModel.findById(req.params.id);
await sendCart(200, isCart, res);
}
catch(error){
const newCart = await cartModel.create({ items: [] });
await sendCart(201,newCart, res);
}
}

Express require a query parameter

Let's say I have a route /ressource. I can call this route with a query parameter /ressource?param=ABCwhich I can retrieve in Node with:
app.get('/ressource', function (req, res) {
const parameter = req.query.param
})
Now, is there a predefined way I can require the parameter which throws an error for request to /ressource without ?param=ABC.
You can use req.query to get the query parameter and use next callback function to throw an error like
app.get('/ressource', function (req, res, next) {
if(!req.query.param) {
const err = new Error('Required query params missing');
err.status = 400;
next(err);
}
// continue
const parameter = req.body.param
})
In express, query is automatically parsed and put into the req.query object, not the req.param object.
So you can access it like this:
const parameter = req.query.parameter;
read req.query on expressjs docs.
There are no predefined way to.
You can choose to check it yourself inside the callback function:
if (!req.query.parameter) {
res.send('parameter is missing');
}
or to use a router middleware which would serve the same purpose

Express API "Create User" operation fires early with undefined values, without waiting for Mongoose Query Middleware to configure those values

I have built a Registration form where there are two Client Usertypes - Master and Child. Master users have their own Database space built out for them using a specific access key.
When a user registers as a Child account, the API has some Middleware make a query for a Master account to bind to and retrieve this Access Key, which is a required field in the Mongoose Schema.
My issue is that - occasionally the middleware will be skipped entirely - or more accurately, it will not actually WAIT for the Query callback to complete, and will immediately fire off next() without awaiting the result of the mongoose query.
This results in a 500 Error since a required Mongoose Schema field has not been retrieved, and the registration process aborts.
Here is the API code and CRUD route:
mongoose.Promise = global.Promise;
router.route('/clients')
.post(register.configureNewUser, clients.createClient);
MIDDLEWARE: register.configureNewUser:
module.exports.configureNewUser = function(req, res, next) {
if (req.body.isEmployee) {
req.body.isMaster = false;
findMasterAndTagChild(req, res, next) // Middleware which needs to run for successful registration
}
}
Subsequent Middleware: (This is the one that is being skipped over before it gets a chance to complete!!):
findMasterAndTagChild = function(req, res, next) {
Client.findOne({
isMaster: true,
organizationName: req.body.employerLookup
},
function(err, boss) {
if (err) return next(err);
req.body.mongoCollectionKey = boss.mongoCollectionKey; // req.body.mongoCollectionKey is undefined in clients.createClient
req.body.organizationName = boss.organizationName;
}).exec().then(() => next()) // next() fires before all code in query callback completes
}
I believe I am using promises incorrectly here. I understand that Mongoose Queries are not Promises, but why is my .then() firing before the Mongoose Query Callback gets a chance to execute all of its code?
Yes, you are using both a callback function and promises. You need to move the callback functionality into the .then callback:
.then(boss => {
req.body.mongoCollectionKey = boss.mongoCollectionKey;
req.body.organizationName = boss.organizationName;
next();
}).catch(err => next(err));
If you're able to use async/await you can write this a bit more cleanly:
findMasterAndTagChild = async function(req, res, next) {
try {
const boss = await Client.findOne({
isMaster: true,
organizationName: req.body.employerLookup
});
req.body.mongoCollectionKey = boss.mongoCollectionKey;
req.body.organizationName = boss.organizationName;
next();
} catch (err) {
next(err);
}
}

Resources