Im currently writing a RESTful API for a webservice but im having trouble. Im trying to delete an mail, but first i want to check if the mail even exists. My problem is that it doesn't check if mail is null and doesn't respond with a 404. Im working with express and mongoose
router.delete('/:id', (req, res) => {
const { id } = req.params;
Mail.findById(id)
.exec()
.then((mail) => {
if (!mail) {
console.log(mail) // returns null
return res.status(404);
}
})
.then(
Mail.deleteOne({ _id: id })
.exec()
.then(() => {
res.status(200).json({
message: 'Mail deleted',
});
})
.catch((err) => {
res.status(500).json({ error: err });
})
);
});
I think you have to do the the deletion part of the code inside the first then block as an else statement. You are not returning anything that the next then block can use.
you could do:
Mail.findById(id)
.exec()
.then((mail) => {
if (!mail) {
console.log(mail) // returns null
return res.status(404).send() //need to send response;
}
Mail.deleteOne({ _id: id })
.exec()
.then(() => {
res.status(200).json({
message: 'Mail deleted',
});
})
}).catch((err) => {
res.status(500).json({ error: err });
})
PRO TIP: if you don't know it, learn async await. Code will look much cleaner!
Then it would look like this:
router.delete('/:id', async (req, res) => {
const { id } = req.params;
try {
const mail = await Mail.findById(id);
if(!mail) {
return res.status(404).send();
}
await Mail.deleteOne({_id: id});
res.status(200).json({
message: 'Mail deleted',
});
} catch(e) {
res.status(500).json({ error: err });
}
Related
I've got an endpoint in my Application that is supposed to fetch data from a MongoDb Database and return the results to the Client. However, before my for(){} loop finishes execution and an empty array ends up getting sent. How do I change my Code where I can send the Array only when it's completely populated.
Code Below:
app.get("/check/classes/usercart", (req, res) => {
if(req.session.user){
UserCart.findOne({userId: req.session.user._id}).lean().exec()
.then((foundUserCart)=>{
if(foundUserCart){
console.log("Found user in the UserCart Collection", foundUserCart);
console.log("Printing User's classCart", foundUserCart.classCart);
//return res.json({classes: foundUserCart.classCart});
const classesArray = foundUserCart.classCart;
let arrayToSend = [];
for(let i = 0; i < classesArray.length; i++){
Classes.findById({_id: classesArray[i]}).lean().exec()
.then((foundClass)=>{
console.log("Class Found", foundClass);
arrayToSend.push(foundClass);
console.log("Printing ArrayToSend", arrayToSend);
})
.catch((error)=>{
console.log("Error performing findById", error);
return res.json({msg: "Server Error"});
});
}
//return arrayToSend;
//This get's executed before my for loop finishes. I'm new to NodeJs and
//the whole asynchronous cycle
return res.json({classes: arrayToSend});
}else{
return res.json({msg: "No records found for this user"});
}
})
.catch((error)=>{
console.log("Error performing UserCart.find() operation", error);
return res.json({msg: "Server Error"});
});
}else{
return res.redirect("/");
}
});
Please could somebody kindly give me some suggestions? Been stuck on this for a while now. Thanks.
Update:
So I got around this by doing the following:
Classes.find({'_id': {$in: classesArray}})
.then((foundRecords)=>{
console.log("Found records", foundRecords);
return res.json({classes: foundRecords});
}).
catch((error)=>{
console.log("Error here", error);
});
This just returns the entire array of records. Probably not the cleanest solution. Could somebody tell me how to do this entire function using Async Await?
You can make your query with multiple IDs in a single operation. That would eliminate the need for the for loop.
const listOfIds = ["46df4667tfs57", "477dfs884v73", "88er58366s"];
const arrayToSend = await Classes.find({ _id: { $in: listOfIds } }).lean().exec();
Using Promise.all
app.get("/check/classes/usercart", (req, res) => {
if (req.session.user) {
return UserCart.findOne({userId: req.session.user._id}).lean().exec()
.then(foundUserCart => {
if (foundUserCart) {
const { classCart } = foundUserCart;
const arrayToSend = classCart.map(_id => Classes.findById({_id}).lean().exec());
return Promise.all(arrayToSend)
.then(classes => res.json({classes}));
}
return res.json({msg: "No records found for this user"});
})
.catch((error)=>{
console.log("Error performing UserCart.find() operation", error);
return res.json({msg: "Server Error"});
});
}
res.redirect("/");
});
Bonus: Rewrite the above using Async/await
app.get("/check/classes/usercart", async (req, res) => {
try {
if (!req.session.user) {
return res.redirect("/");
}
const foundUserCart = await UserCart.findOne({userId: req.session.user._id}).lean().exec();
if (!foundUserCart) {
return res.json({msg: "No records found for this user"});
}
const { classCart } = foundUserCart;
const arrayToSend = classCart.map(_id => Classes.findById({_id}).lean().exec());
const classes = await Promise.all(arrayToSend);
res.json({classes});
} catch (error) {
res.json({msg: "Server Error"});
}
});
Use async/await to clean your code. Your final controller will look like this
app.get('/check/classes/usercart', async (req, res) => {
try {
if (!req.session.user) return res.redirect('/')
const userCart = await UserCart.findOne({ userId: req.session.user._id }).lean()
if(!userCart) throw new Error('User Not Found!')
const classes = await Classes.find({ _id: { $in: userCart.classCart } }).lean()
return res.json({ classes })
} catch (error) {
console.log('Error occured', error)
res.json({ message: 'Error Occured!', error })
}
})
I am trying to find the tasks assigned to the user through this
router.get('/all', auth, async (req, res) => {
try {
const assignments_raw = await Assignment.find({listP: listP.indexOf(req.user.userId)})
res.json(assignments_raw)
} catch (e) {
res.status(500).json({ message: 'Something went wrong, try again' })
}
})
Specifically, this line should have found all the tasks that have an element corresponding to the user ID inside the listP field
const assignments_raw = await Assignment.find({listP: listP.indexOf(req.user.userId)})
But this causes an error, why?
below is an excerpt from Mango
You can do like this
router.get('/all', auth, async (req, res) => {
try {
const assignments_raw = await Assignment.find()
let assignments = []
assignments_raw.map(assignment => {
if (assignment.listP.indexOf(req.user.userId) !== -1) {
assignments.push(assignment)
}
}
)
res.json(assignments)
} catch (e) {
res.status(500).json({ message: 'Something went wrong, try again' })
}
})
I am trying to implement the validation of password change and the issue I have is that I am not getting errorMessage back from the server in case of an error. I have managed to get it work and send back response after the password was updated. Also, I can console.log the error message on the back end but it's not returning an object with errorMessage to the front end.
if (!currentPassword) {
console.log("no current password");
return res
.status(400)
.json({ errorMessage: "Please confirm your current password" });
}
On the front code looks like this:
handleSubmit = (event) => {
event.preventDefault();
const authorization = localStorage.getItem("accessToken");
axios
.put(
`${process.env.REACT_APP_SERVER_URL}/settings/password`,
this.state.user,
{
headers: {
authorization,
},
}
)
.then((res) => {
if (res.errorMessage) {
console.log(res, "Unsuccessful password updated");
} else {
console.log("updating - res:", res);
this.setState({
user: res.data,
});
}
})
.catch((err) => {
console.log(err, "ERROR");
});
};
Everytime there is an error, I am not consol login the actual erroMessage but it is being catched in catch. What is the cause of that?
Thanks
Not a direct res its available under res.data.
Response schema of axios
use
if (res.data.errorMessage) {
instead of
if (res.errorMessage) {
For better understanding you need to console.log(res). Then you could understand the structure of the response
router.put("/password", isLoggedIn, (req, res, next) => {
const { currentPassword, newPassword, newPasswordConfirm } = req.body;
User.findById(req.user._id)
.then((user) => {
bcrypt.compare(currentPassword, user.password).then((isSamePassword) => {
if (!isSamePassword) {
console.log(
"Incorrect current password. To change your password try again!"
);
return res.status(400).json({
errorMessage:
"Incorrect current password. To change your password try again!",
});
}
return bcrypt
.genSalt(saltRounds)
.then((salt) => bcrypt.hash(newPassword, salt))
.then((hashedPassword) => {
User.findByIdAndUpdate(
req.user._id,
{ password: hashedPassword },
{ new: true }
)
.then((user) => {
console.log("user's password successfully changed");
res.status(200).json(user);
})
.catch((err) => {
res.status(500).json({ errorMessage: err.message });
});
})
.catch((err) => {
res.status(500).json({ errorMessage: err.message });
});
});
})
.catch((err) => {
console.log(err);
res.status(500).json({ errorMessage: err.message });
});
});
I want to replace promises chain inside my Express routing with async/await. This makes code clean and more readable. First look at my code.
What i've had before:
app.post('/search', (req,res) => {
sendRequest(req.body).then( searchDetails => {
res.send(searchDetails);
}).catch( error => {
res.status(404).send(error)
});
});
Current code:
app.post('/search', asyncMiddleware(async (req,res) => {
const result = await sendRequest(req.body);
res.send(result);
}));
And this how looks asyncMiddleware:
const asyncMiddleware = checkedFunction => (req, res) => {
Promise
.resolve(
checkedFunction(req, res)
)
.catch( error => {
res.status(400).send(error)
});
};
The problem starts when I have routing which includes more than one error status.
app.delete('/delete/:id', authenticate, (req, res) => {
const id = req.params.id;
if (!ObjectID.isValid(id)) {
return res.status(404).send();
}
User.findOneAndDelete({
_id: id,
_user: req.user._id
}).then((todo) => {
if (!todo) {
return res.status(404).send();
}
res.send({todo});
}).catch((e) => {
res.status(400).send();
});
});
How can I make to asyncMiddleware will return status depends on error?
asyncMiddleware here checks if any error has occured or is deliberately thrown by checkedFunction, namely express route handler. If you would like to say something to asyncMiddleware, you need to wrap your route handler with it as you did for /search, then you need to throw specific errors/objects involving your error information:
app.delete('/delete/:id', authenticate, asyncMiddleware(async (req, res) => {
const id = req.params.id;
if (!ObjectID.isValid(id)) {
throw {
status: 404,
message: 'id not valid'
}
}
try {
const todo = await User.findOneAndDelete({
_id: id,
_user: req.user._id
});
if (!todo) {
throw {
status: 404,
message: 'todo not found'
}
}
res.send({todo});
} catch (e) {
throw {
status: 400,
message: 'mongodb error'
}
}
}));
then asyncMiddleware can send status in response
const asyncMiddleware = checkedFunction => (req, res) => {
Promise
.resolve(
checkedFunction(req, res)
)
.catch( error => {
res.status(error.status).send(error.message)
});
};
You can create built-in Error objects instead of custom ones to track error call stack but I don't think you need here.
Hello I would like to update a document in my db using findByIdAndUpdate only doing minimal calls, but some values have to be pushed onto an array and other updated.
i'm sure there must be an easy way to make this into one route instead of using the two
router.put('/notes/:id', (req, res) => {
Player.findByIdAndUpdate({
_id: req.params.id
}, {
$push: {
notes: req.body.notes
}
}, {
new: true
})
.then(player => res.status(200).json(player))
.catch(err => res.status(400).json({
'err': 'updating went wrong'
}))
})
router.put('/:id', (req, res) => {
let updates = {};
if (req.body.first) {
updates.first = req.body.first;
}
if (req.body.last) {
updates.last = req.body.last;
}
if (req.body.school) {
updates.school = req.body.school;
}
if (req.body.rank) {
updates.rank = req.body.rank;
}
Player.findByIdAndUpdate({
_id: req.params.id
}, updates, {
new: true
})
.then(player => res.status(200).json(player))
.catch(err => res.status(400).json({
'err': 'updating went wrong'
}))
})