I have an async function
async function getPostAsync() {
const post = await Post.findById('id');
// if promise was successful,
// but post with specific id doesn't exist
if (!post) {
throw new Error('Post was not found');
}
return post;
}
I am calling the function with
app.get('/', (req, res) => {
getPostAsync().then(post => {
res.json({
status: 'success',
});
}).catch(err => {
res.status(400).json({
status: 'error',
err
});
})
});
but I just receive
{
"status": "error",
"err": {}
}
I would expect to either get the error Post was not found or some error with the connection or something like that, but the variable err is simply an empty object in my catch statement.
Consider the following:
let e = Error('foobar');
console.log( JSON.stringify(e) )
This outputs {}, much like in your case. That's because errors don't serialize to JSON very well.
Instead, try this:
res.status(400).json({
status : 'error',
err : err.message // `String(err)` would also work
});
Related
I am new to Node JS. I am practising to build a MERN app. I have two function getUserById and isSignedIn. These two function are middlewares.
router.param("userId",getUserById)
const getUserById = (req,res,next,id)=>{
User.findById(id).exec((err,user)=>{
if(err){
return res.json({
error:"Unable to process request",
status: false,
})
}
console.log(1234);
if(!user){
return res.json({
error:"User doesn't exist",
status: false
})
}
req.user=user;
next()
})
}
const isSignedIn = (req,res,next)=>{
const token = req.headers.authorization.split(" ")[1]
jwt.verify(token, process.env.SECRET_KEY, (err, decoded)=>{
if(err){
return res.json({
status: false,
error: "Invalid Token"
})
}
console.log(123);
req.auth=decoded
next()
})
};
router.post("/api/create/:userId",isSignedIn,(req,res)=>{ res.send("Success")})
This is my understanding. If in url userId is found getUserById will be executed and then isSigned in. In getUserById if there was an error or if the user doesn't exist if will send a response and the execution of code stop there. But in isSignedin if the token is not valid I am sending a response as Invalid Token and the code execution should stop there. But the code after if is also getting executed why it is so?
Try following code
router.param("userId",getUserById)
const getUserById = async (req,res,next,id)=>{
try {
const user = await User.findById(id)
if(!user){
return res.json({
error:"User doesn't exist",
status: false
})
} else {
req.user=user;
next()
}
} catch (err) {
return res.json({
error:"Unable to process request",
status: false,
error: JSON.stringify(err),
})
}
}
const isSignedIn = async (req,res,next)=>{
const token = req.headers.authorization.split(" ")[1]
try {
const decoded = await jwt.verify(token, process.env.SECRET_KEY)
if (decoded) {
req.auth=decoded
next()
}
} catch (err) {
if(err){
return res.json({
status: false,
error: "Invalid Token"
})
}
}
};
router.post("/api/create/:userId",isSignedIn,(req,res)=>{ res.send("Success")})
I updated mongoose to latest version (6.0.2) and now I'm recieving this error and crush the application whenever .updateOne() is executed. But object update inside the database. My code:
async(req,res) => {
await Todo.updateOne(
{_id : req.params.id},
{
$set : {
status : "inactive"
}
},
(updateError) => {
// if updateError exist
if (updateError) {
// print error
printError(updateError);
// response the error
res.status(500).json({
error : "There was a Server Side Error!"
});
} else {
// if error dose not exist
// print message
console.log("|===> 🗂️ Data Was Updated successfully! 🗂️ <====|\n");
// response success
res.json({
message : "Todo Was Update successfully!"
});
}
});
}
Try to add .clone() to de updateOne function
async(req,res) => {
await Todo.updateOne(
{_id : req.params.id},
{
$set : {
status : "inactive"
}
},
(updateError) => {
// if updateError exist
if (updateError) {
// print error
printError(updateError);
// response the error
res.status(500).json({
error : "There was a Server Side Error!"
});
} else {
// if error dose not exist
// print message
console.log("|===> 🗂️ Data Was Updated successfully! 🗂️ <====|\n");
// response success
res.json({
message : "Todo Was Update successfully!"
});
}
}).clone();
}
Latest version of mongoose not duplicate execution
https://mongoosejs.com/docs/migrating_to_6.html#duplicate-query-execution
Since you are using async syntax, put your code in try/catch blok:
async (req, res) => {
try {
await Todo.updateOne({ _id: req.params.id }, { $set: { status: "inactive"}});
res.status(200).json({message: "Todo Was Update successfully!"});
} catch (error) {
res.status(500).json({error:'There was a Server Side Error!'})
}
}
Your async/await should look like this:
async (req,res) => {
try {
const result = await Todo.updateOne(
{_id : req.params.id},
{
$set : {
status : "inactive"
}
},
);
}
console.log('success', result)
res.json({message: "Todo Was Update successfully!", result })
} catch (err) {
console.log('error', err)
res.status(500).json({error:'There was a Server Side Error!'})
}
}
exports.confirm_new_app = (req, res) => {
//get the app
AppReq.findOne({_id: req.body.appId})
.then( (app) => {
if (app) {
//remove additional info(reqDate,reqUserId)
app = app.toObject()
delete app.reqDate
delete app.reqUserId
//add it to apps collection
let newApp = new App(app)
return newApp.save()
} else {
//how to send this response??????
res.status(404).json({
message: "No application with the provided app Id!"
})
}
})
.then( (savedApp) => {
//remove the savedApp from appReq collection
return AppReq.remove({_id: savedApp._id})
}).then( (removedApp) => {
res.status(200).json(removedApp)
})
.catch( (err) => {
res.status(500).json({
error: err
})
})
}
what is the proper way of sending res.status(404).json({...}) in the example above?
The current code will trigger an error because it attempts to send response twice!!
Throw an error any time you have an error and want to exit the .then() chain. Then handle the errors in your catch.
So instead of
else {
//how to send this response??????
res.status(404).json({
message: "No application with the provided app Id!"
})
}
try:
else {
throw new Error('appId');
}
then in your catch:
.catch( (err) => {
if (err.message === 'appId') {
res.status(404).json({
message: "No application with the provided app Id!"
})
} else {
res.status(500).json({
error: err
})
}
})
I cannot catch an error I throw in Node and catch it in mocha.
Please help!
Node:
Node code that throws error
app.put('/reviews/:id', (req, res) => {
var db = req.db;
Review.findById(req.params.id, 'title description', function (error, review) {
if (error) {
// console.error(error);
throw new Error('some error');
}
review.title = req.body.title;
review.description = req.body.description;
review.save(function (error) {
if (error) {
console.log(error)
}
res.send({
success: true
})
})
})
})
Mocha:
Mocha test
it('should throw exception for incorrect id', function (done) {
let updateReview = {
title: 'Updated Title',
description: 'Updated Description'
};
chai.request(app)
.get('/reviews')
.end(function (err, res) {
chai.request(app)
.put('/reviews/12')
.send(updateReview)
.end(function (err, res) {
expect(app).to.throw(Error);
done();
});
});
});
Error:
Error from the console.
Uncaught Error: some error
You can't expect an error to be thrown at the end of your http call.
You could test the return code for a specific request by adding a response instead of the throw line as the following :
app.put('/reviews/:id', (req, res) => {
var db = req.db;
Review.findById(req.params.id, 'title description', function (error, review{
if (error) {
// console.error(error);
return res.sendStatus(500);
}
review.title = req.body.title;
review.description = req.body.description;
review.save(function (error) {
if (error) {
console.log(error)
}
res.send({
success: true
})
})
})
And by updating your test :
it('should throw exception for incorrect id', function (done) {
let updateReview = {
title: 'Updated Title',
description: 'Updated Description'
};
chai.request(app)
.get('/reviews')
.end(function (err, res) {
chai.request(app)
.put('/reviews/12')
.send(updateReview)
.end(function (err, res) {
expect(res).to.have.status(500);
done();
});
});
});
If you want to test that a specific error is thrown for a given function, then you should call expect with the function as parameter. This could be done by isolating your database call in a function, for instance.
The .throw assertion only works on functions. More specifically: it should be passed a function reference, and when Chai calls that function, it expects that the function throws an error.
In your situation, app isn't a function, it's a reference to an Express application (it may be callable, but you're not testing the app function, you're testing how the app handles a particular request).
Throwing an error inside an Express app triggers Express's error handler. It will handle the error by sending a HTTP 500 response.
You can test for this:
expect(res).to.have.status(500);
I'm scratching my head here, I'm using bcrypt to try and log in a user but I'm getting the following error:
Error: Can't set headers after they are sent.
Here's the code for that route:
router.post('/login', (req, res, next) => {
User.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length < 1) {
res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
res.json(200).json({
message: 'Auth successful'
});
}
return res.status(401).json({
message: 'Auth failed 3'
});
});
})
.catch(err => {
res.status(500).json({
error: err
});
});
});
I thought the error might've come from the if else statements and trying to send a header twice, but I'm ending them before moving on the next conditional no? Am I miss-reading something here?
The error you see is caused when your code tries to send more than one response for the same request. The typical cause for this error is a coding mistake in how you handle asynchronous operations. In your code you show, I can see the following mistakes that can cause this error:
If user.length < 1, then you do res.status(401).json(...), but then you let the code continue to run where you then send other responses.
If you get an error from bcrypt.compare(), you send an error response and then let the code continue to run and send other responses.
If bcrypt.compare() succeeds, you send res.json(200).json(...) which is just wrong. You probably meant res.status(200).json(...).
If bcrypt.compare() succeeds and you have a result, you send two responses.
In looking at your code, it appears that you think that as soon as you do res.json() that the function returns and no other code executes. That is not the case. Until you hit a return, the rest of the code in that function continues to execute.
Here's one way to fix it (adding a few return statements and one else):
router.post('/login', (req, res, next) => {
User.find({ email: req.body.email }).exec().then(user => {
if (user.length < 1) {
res.status(401).json({message: 'Auth failed'});
return;
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
res.status(401).json({message: 'Auth failed'});
return;
}
if (result) {
res.json({message: 'Auth successful'});
} else {
res.status(401).json({message: 'Auth failed 3'});
}
});
}).catch(err => {
res.status(500).json({error: err});
});
});
Or, to do this in a little cleaner fashion where all responses with the same status are combined and all flow control is done with promises, you can do something like this:
const util = require('util');
bcyrpt.compareAsync = util.promisify(bcrypt.compare);
router.post('/login', (req, res, next) => {
User.find({ email: req.body.email }).exec().then(user => {
if (user.length < 1) {
throw new Error('No user match');
}
return bcrypt.compareAsync(req.body.password, user[0].password).then(result =>
if (!result) {
throw new Error('Auth failed 2');
}
res.json({message: 'Auth successful'});
}
}).catch(err => {
res.json(401).json({message: err.message});
});
}).catch(err => {
res.status(500).json({error: err});
});
});