How to update subdoc data in Mongoose - node.js

I am trying to update user.task.fixitItem, where the Task schema is embedded within the User schema.
Using this get,
app.get('/api/tasks/:id/edit', isAuthenticated, function (req, res) {
console.log('*** testing route GET /api/tasks/:id/edit', req.params);
User.findOne({'task._id': req.params.id})
.select('task.$')
.exec(function(err, user) {
if(!user) {
res.statusCode = 404;
return res.send({ error: 'Not found' });
}
if(!err) {
return res.render('tasks/edit', {task: user.task[0] });
} else {
res.statusCode = 500;
console.log('Internal error(%d): %s', res.statusCode, err.message);
return res.send({ error: 'Server error' });
}
}
);
});
How do you write the put to update the data?

You need to use the update method with $set
User.update(
{ 'task._id': req.params.id },
{ $set: { 'task.$.fixitItem': 'new value' }},
function(err, user) {
}
);

Related

TEST MONGOOSE "findById" WITH SINON

I'm using Moncha and Sinon in a node.js project with mongoose.
How could I test the 3 different statement in findById callback?
exports.findUserById = (req, res) => {
const id = req.params?.id;
if (!mongoose.Types.ObjectId.isValid(id)) {
res.status(400);
res.send({ message: "not valid id" });
return;
}
UserModel.findById(id, function (err, doc) {
if (err) {
res.status(500).send({
message: err.message,
});
} else if (!doc) {
res.status(404).send({
message: ` user with id: ${id} not found`,
});
} else {
res.status(200).send(doc);
}
});

Can't see where multiple call of res caused the error : Cannot set headers after they are sent to the client

I'm following a tutorial in the net. It's a MERN project with mongo/mongoose. When I have implemented the update function in the controller the following error has occured :
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I've seen the answers about similar issue where they say it's because there are 2 or multiple calls of res (res.json(), res.send() etc..), but I don't see where must I change this in the following function :
module.exports.updateUser = async(req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
try {
await UserModel.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
bio: req.body.bio
}
},
(err, docs) => {
if (!err)
return res.send(docs);
if (err)
return res.status(500).send({ message: err });
}
)
} catch (err) {
return res.status(500).json({ message: err });
}
};
It may be that you've mixed up two different error handling patterns.
You don't need try/catch if you're using built in error handling of findOneAndUpdate()
await UserModel.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
bio: req.body.bio
}
},
(err, docs) => {
if (!err)
return res.send(docs);
if (err)
return res.status(500).send({ message: err });
}
)
and if you are using try/catch, you don't need findOneAndUpdate's error handling:
try {
const user = await UserModel.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
bio: req.body.bio
}
})
return res.send(user)
} catch (err) {
return res.status(500).json({ message: err });
}
Could you please change code like this:
module.exports.updateUser = async(req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
try {
const result = await UserModel.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
bio: req.body.bio
}
});
return res.send(result);
} catch (err) {
return res.status(500).json({ message: err });
}
};

Update database

I am struggling to get .put or .patch to work. when using postman I get the call back returned but the values are not updated in my database on robo 3t. I have tried fixing the deprecation warning and using updateOne, updateMany.
This will fix the deprecation warning but will not update the database. Here is the code before i fix the deprecation. Any ideas what I'm doing wrong?
////////////////////Request Targeting A Specific Article///////////////////////
app.route("/articles/:articleTitle")
.get(function(req, res){
Article.findOne({title: req.params.articleTitle}, function(err, foundArticle){
if (foundArticle) {
res.send(foundArticle);
} else {
res.send("No articles with that title.");
}
});
})
/////////PUT PROBLEM MUST BE FIXED /////////////
.put(function(req, res){
Article.update(
{title: req.params.articleTitle},
{title: req.body.title, content: req.body.content},
{overwrite: true},
function(err){
if(!err){
res.send("succesfully updated");
}
}
);
})
///////PATCH PROBLEM MUST BE FIXED ///////////
.patch(function(req, res){
Article.update(
{title: req.params.articleTitle},
{$set: req.body},
function(err){
if(!err){
res.send("Successfully updated article.");
} else{
res.send(err);
}
}
);
});
app.route("/articles/:articleTitle")
.get((req, res) => {
Article.findOne({ title: req.params.articleTitle }, (err, result) => {
if (result) {
res.send(result);
} else {
res.send("err");
}
});
})
.put((req, res) => {
Article.replaceOne(
{ title: req.params.articleTitle },
{ title: req.body.title, content: req.body.content },
{ overwrite: true },
(err) => {
if (err) {
res.send("There is some error");
} else {
res.send("Updated successfully");
}
}
);
})
.patch((req, res) => {
Article.updateOne(
{ title: req.params.articleTitle },
{ $set: req.body },
(err) => {
if (err) {
res.send("There is some error");
} else {
res.send("Updated successfully");
}
}
);
});
Try this!! this will work fine.

Very similar Node routes - any better solution?

I have two very similar routes (two API results). So one for grabbing the email address and one for grabbing their username. I'm using these two separately, one for validating the email address by using an AJAX call and another for validating the username (basically checking if either exist).
Is there any way in Node/Express/Mongoose to specifically check both (at seperate times) without having to have two methods, it just feels so redundant?
Used to check for email validation (that it doesn't already exist)
router.get('/:id', function(req, res) {
var emailAddress = req.params.id;
User.find({ 'emailAddress': emailAddress }, function (err, user) {
if(!user) {
res.statusCode = 404;
return res.json({
error: 'Not found'
});
}
if (!err) {
if (user[0]!=undefined) {
return res.json(true);
} else {
return res.json({
error: 'Not found'
});
}
} else {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({
error: 'Server error'
});
}
});
});
Used to check for username validation (that it doesn't already exist)
router.get('/username/:id', function(req, res) {
var username = req.params.id;
User.find({ 'username': username }, function (err, user) {
if(!user) {
res.statusCode = 404;
return res.json({
error: 'Not found'
});
}
if (!err) {
if (user[0]!=undefined) {
return res.json(true);
} else {
return res.json({
error: 'Not found'
});
}
} else {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({
error: 'Server error'
});
}
});
});
Just returning boolean's essentially for both results. Surely there's a better way than this? There's just so much duplicated code it's frustrating.
Edit: still researching but maybe I could use User.find() and pass in what I want but in terms of passing that from the API to the user Model, how is that possible?
Split them in to reusable units and use them in the routes.
function isUserExists(query, callback) {
User.findOne(query, function(error, user) {
if(error) {
return callback(error);
}
callback(null, !!user);
});
}
function sendResponse(res, error, result) {
if(error) {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({error: 'Server error'});
}
if(!user) {
res.statusCode = 404;
return res.json({
error: 'Not found'
});
}
res.json(result);
}
router.get('/:id', function(req, res) {
isUserExists({emailAddress: req.params.id}, sendResponse.bind(null, res));
});
router.get('/username/:id', function(req, res) {
isUserExists({username: req.params.id}, sendResponse.bind(null, res));
});

Value not being updated in DB, node.js and mongoose

I am trying to update the value of my model and it does not work.
The weird thing is that I am printing out the result and it looks different than what I see in my database by using Robomongo.
Any thoughts why this happens?
Here is my code:
exports.create = function(req, res) {
var productId = req.query.product;
if (productId) {
Request.createWizard(req.user, { productId: productId }, function(err, request) {
Product.findById(productId, function(err, product) {
if (err) {
return console.log('oh no! error', err);
} else {
if (product.price =! 0 )
request.status = 'ready';
console.log(request);
(Here I see in the terminal: status = ready)
}
});
req.flash('success', { msg: 'Your request has been successfully created.' });
res.redirect('/discover');
});
} else {
var pages = require('../../schemas/wizard/request')();
res.render('requests/form', {
title: 'Make a Request',
pages: pages,
saveState: false
});
}
};
When I am checking the database status is still on pending.
You're changing the status property, but you're not saving the document back to the database after doing so:
Request.createWizard(req.user, { productId: productId }, function(err, request) {
Product.findById(productId, function(err, product) {
if (err) {
return console.log('oh no! error', err);
} else {
if (product.price !== 0) {
request.status = 'ready';
request.save(function(err) { // <-- save it back to the database
if (err) {
console.log('oh no! error', err);
} else {
console.log(request);
}
});
}
}
});
req.flash('success', { msg: 'Your request has been successfully created.' });
res.redirect('/discover');
});

Resources