I'm learning back-end web development and the course I'm following includes making a REST API for a system that has "tours". I use MongoDB and Mongoose and everything was fine, I was filtering the query according to the parameters and that went well. But then the instructor wanted to put this filtering process (and other query processes) into a class and access them inside this class. After I've done this, I started getting this error. So I know that you can't convert a circular object to JSON, nor can you stringify such an object. But I don't have any... circular objects. Or at least I don't think so. I'm just a beginner, so forgive my mistakes if you will. Here's the code:
const Tour = require("../models/tourModel"); //MongoDB Document Model
class QueryModifier {
constructor(query, reqQuery) {
this.query = query;
this.reqQuery = reqQuery;
}
filter() {
console.log(this.reqQuery);
const queryObj = { ...this.reqQuery }; //destruct the URL query object into a new object for a hard-copy
const toBeExcluded = ["page", "sort", "limit", "fields"];
toBeExcluded.forEach((el) => {
delete queryObj[el];
});
console.log(queryObj);
let queryStr = JSON.stringify(queryObj);
queryStr = queryStr.replace(
/\b(gte|gt|lte|lt)\b/g,
(matchedStr) => `$${matchedStr}`
);
this.query = this.query.find(JSON.parse(queryStr));
return this;
}
sort() {
if (this.reqQuery.sort) {
const sortBy = this.reqQuery.sort.replace(/(,)/g, " ");
this.query = this.query.sort(sortBy);
} else {
this.query = this.query.sort("-createdAt");
}
return this;
}
limitFields() {
if (this.reqQuery.fields) {
const fields = this.reqQuery.fields.replace(/(,)/g, " ");
this.query = this.query.select(fields);
} else {
//Default
this.query = this.query.select("-__v"); //exclude __v
}
return this;
}
paginate() {
const page = this.reqQuery.page * 1 || 1;
const limit = this.reqQuery.limit * 1 || 10;
const skip = (page - 1) * limit;
this.query = this.query.skip(skip).limit(limit);
return this;
}
}
exports.getAllTours = async (req, res) => {
try {
const features = new QueryModifier(Tour.find(), req.query)
.filter()
.sort()
.limitFields()
.paginate();
const tours = await features;
res.status(200).json({ //<------ Error's "position" is after the second dot.
status: "success",
results: tours.length,
data: { tours },
});
} catch (e) {
res.status(404).json({
status: "failure",
message: e.message,
});
console.log(e);
}
};
The error is at the place where I commented with an arrow. And here's the error message:
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Topology'
| property 's' -> object with constructor 'Object'
| property 'sessionPool' -> object with constructor 'ServerSessionPool'
--- property 'topology' closes the circle
at JSON.stringify (<anonymous>)
at stringify (/Users/mirzabicer/Documents/GitHub/complete-node-bootcamp/4-natours/starter/node_modules/express/lib/response.js:1123:12)
at ServerResponse.json (/Users/mirzabicer/Documents/GitHub/complete-node-bootcamp/4-natours/starter/node_modules/express/lib/response.js:260:14)
at exports.getAllTours (/Users/mirzabicer/Documents/GitHub/complete-node-bootcamp/4-natours/starter/controllers/tourController.js:84:25)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
I edited that JSON.stringify call to:
let queryStr = '{ "sort": "price,ratingsAverage", "price": { "gte": "300" } }';
So there's literally no JSON.stringify call in my code now. But the error I get is EXACTLY the same.
CodePudding user response:
The error suggests the problem is in tourController.js
on line 84. I guess you are calling JSON.stringify
on something that is circular on that line. Try removing that JSON.stringify
call.
It seems tours
is most likely the problem then. I see it is a QueryModifier
. I suppose that is probably not the data
you want to return from getAllTours
.
You should await the features.query
, not the features
itself.