With the recent patch to handlebars, pages began returning errors anywhere I had referenced a mongoose document.
Handlebars: Access has been denied to resolve the property “email” because it is not an “own property” of its parent
So, looking up the error, I get this page, which seems more focused on how to go around the patch rather than complying with it. So, I check stack-overflow to see what my fellow dev's are doing. I found (mostly) just people explaining how to get around it. A few astute comments pointed out we just need a new object, were hasOwnProperty is true for the values being referenced.
It occurred to me that I'd seen .toObject()
in mongoose when I'd created virtuals for a few of my models. For those unfamiliar, when creating virtuals it's generally advised you pass a configuration object with the schema that looks like the following:
{
toObject: {
virtuals: true,
},
toJSON: {
virtuals: true,
},
}
This means that a user schema might look like this:
const UserSchema = new mongoose.Schema({
birthday: {
type: Date,
format: 'MMMM Do YYYY',
},
secondLanguage: {
type: String,
default: 'None',
},
email: {
type: String,
},
}, {
toObject: {
virtuals: true,
},
toJSON: {
virtuals: true,
},
});
With that in place, we have access to the .toObject() method on documents.
So, in our routes (using Express in this example but the idea remains the same regardless):
router.get('/dashboard', async(req, res) => {
try {
const user = await User.findOne({email: req.body.email.toLowerCase()});
res.render('index/dashboard', {
user: user.toObject(),
})
} catch (err) {
console.error(err);
}
}
Places where you did .find() you simply forEach and run .toObject on each document. I ended up making some wrapping functions:
const multipleMongooseToObj = (arrayOfMongooseDocuments) => {
const tempArray = [];
if (arrayOfMongooseDocuments.length !== 0){
arrayOfMongooseDocuments.forEach(doc => tempArray.push(doc.toObject()));
}
return tempArray;
};
const mongooseToObj = (doc) => { if (doc == null){ return null; } return doc.toObject(); };
module.exports = {
mongooseToObj,
multipleMongooseToObj,
};
These can be used as follows:
const user = mongooseToObj(await User.findOne({email: req.body.email.toLowerCase()})); // Returns the same as user.toObject()
const users = multipleMongooseToObj(await User.find()); // Return arrays where .toObject() was called on each document
Top comments (4)
Oh my god, thank you so much for this! I couldn't even get the workarounds to work for my setup.
In the end I didn't even have to add the toObject() to my Mongoose Schema. I just added that method to the req.locals in my server.js file.
Just what I needed! Thank you so much!
I had this problem.
I decided to change from yarn to npm, and it worked for my project.
How to get id instead of _id?