Sending custom error from Node/Express to Angular - node.js

I am sending an HTTP request from angular to a node/express API. How do I get the actual error message I send from the node/express API
I am handling my exceptions well when they are thrown in the API but the moment I try to read the message in Angular I only get the name of the type of error I throw. For example, if I throw a 409 the error received by angular is just "Conflict" and does not contain the details I send. Please look at my code below.
I am sending my request as below
register(user: UserAccount) {
return this.http
.post(`${config.apiUrl}/users/register`, user)
.pipe(
map((res: HttpResponse<Response>) => {
return res;
}))
.pipe(catchError(err => this.errorHandler.handleError(err)));
}
My handle error is as below:
handleError(error: HttpErrorResponse) {
console.log(error);
if (error) {
let errMessage = '';
try {
errMessage = error.message;
} catch (error) {
errMessage = error.statusText;
}
return throwError(errMessage || error || 'Server error');
}
return throwError(error.error || error || 'Server error');
}
This how I am throwing my error when I occurs in my Node/Express API
registerUser (req, res) {
debug(chalk.blue(`*** insert user`))
userRepo
.create(req.body)
.then(user => {
debug(chalk.green(`*** Insert User ok!`))
res.status(200).json({
status: true,
error: null,
user: user
})
})
.catch(err => {
debug(chalk.red(`*** insertUser error: ${util.inspect(err)}`))
if (err['type'] && err['type'] === '409') {
res.status(409).json({
status: false,
error: err['message'],
user: null
})
} else {
res.status(400).json({
status: false,
error: err,
user: null
})
}
})
}
I want to be able to receive the json object with the information about the error but all I am getting when I access the error item is, for example, in the case of raising a 409, I only get 'Conflict'

The reason for this is that when you catch the error and the status is a 409, you return `err['message'] instead of 'err'.
So instead of:
res.status(409).json({
status: false,
error: err['message'],
user: null
})
You should return:
res.status(409).json({
status: false,
error: err,
user: null
})
This is actually what you do in the case of a 400 error!

Related

Cannot set headers after they are sent to the client in Express js

I'm getting this error while running this code. The API is running fine, after that it is throwing this error.
app.post('/api', async (req, res) => {
try {
channelTwo.consume(queue, async (data) => {
if (data) {
const _data = JSON.parse(data.content);
const SavedData = new User(_data);
await SavedData.save();
channelTwo.ack(data);
if (SavedData) {
res.status(200).json({ message: 'Date Saved to db', SavedData });
}
res.status(400).json({ message: 'Cant save data' });
}
res.status(400).json({ message: 'Cant find data' });
});
} catch (error) {
res.status(400).json({ message: error });
}
})
;
You have to return after calling res.status(<>).json() otherwise it will be called multiple times.
This will try to set a response header code although already json body has already been sent causing your error

Bcrypt password compare not showing results

I have come accross a strange problem. I have an if statement inside the bcrypt.compare(), which doesnt run at all.
Example
bcrypt.compare(req.body.password, data.password, function (err, result) {
if (!result || err) {
res.status(422).json({
message: "Wrong Password",
status: false,
statusCode: 422
})
}
});
const otherData = await findOne({
x : req.body.x
})
if(otherdata.x == "dummy") {
return res.status(200).json({
message: "wohhooo"
})
}
When i send wrong password in request body it should respond with message: "wrong password"
But it skips that if statement inside bcrypt.compare() and responds with message: "wohhoo"
In console I see, Error: Can't set headers after they are sent. with error pointing to return statement inside bcrypt.compare
[bcrypt.compare]1 is asynchronous function, So your program is executing res.status(200).json({message: "wohhooo"}) before bcrypt.compare
// Quick Fix
bcrypt.compare(req.body.password, data.password, function (err, result) {
if (!result || err) {
return res.status(422).json({
message: "Wrong Password",
status: false,
statusCode: 422
})
} else {
const otherData = await findOne({
x: req.body.x
})
if (otherdata.x == "dummy") {
return res.status(200).json({
message: "wohhooo"
})
}
}
});
Reference :
What the heck is a Callback?

Cannot read property 'error' of undefined. What's the problem?

My REST API reasons this: {"error":"Auth failed. User does not exist"}.
I try save this error to my state using setState in React, but I have a this error: Cannot read property 'error' of undefined. What's the problem?
export const login = user =>
axios
.post('http://localhost:3000/api/users/login', {
login: user.login,
password: user.password,
})
.then(res => {
localStorage.setItem('userToken', res.data.token);
return res.data.token;
})
.catch(err => {
console.log(err);
});
Function in React.js:
onSubmit(e) {
e.preventDefault();
const user = {
login: this.state.login,
password: this.state.password,
};
login(user).then(res => {
if (!res.error) { // <- this error: TypeError: Cannot read property 'error' of undefined
this.props.history.push(`/dashboard`);
} else {
this.setState({ error: res.error });
}
});
}
This is my backend code:
// Login Action
...
return res.status(200).json({
message: 'Auth successful',
token,
});
}
res
.status(400)
.json({ error: 'Auth failed. The password is incorrect.' });
} else {
res.status(400).json({ error: 'Auth failed. User does not exist' });
}
})
.catch(err => {
res.status(400).json({ error: err });
});
};
Try this:
login(user).then(() => this.props.history.push(`/dashboard`))
.catch(error=>this.setState({ error })
But maybe there is another problem, you cannot normally push to state the way you do for immutability concern. I guess you know, but I post in case:
this.setState({ history: [...this.state.history, `/dashboard`] })
since your backend is returning a response with 400 status code, you have to handle that in the catch block of your login function. right now you are writing the error into the console and not returning anything, that is why your login response is undefined in your React code and you are getting that error.
to fix this, change the catch block of your login function so it looks something like this
.catch(err => {
console.log(err);
return {error:err};
});
Try this;
export const login = user =>
axios
.post('http://localhost:3000/api/users/login', {
login: user.login,
password: user.password,
})
.then(res => {
localStorage.setItem('userToken', res.data.token);
})
.then(() => return localStorage.getItem('userToken');)
.catch(err => {
console.log(err);
});

Getting 400 Bad Request despite catching the err in angular2+

I have designed a login page where login is successful when i put a correct login and password and Login doesn't happen when I put an incorrect username or password which is correct. However, I get this error:
POST http://localhost:3003/login/authenticate 400 (Bad Request)
ERROR HttpErrorResponse {headers: HttpHeaders, status: 400, statusText: "Bad Request", url: "http://localhost:3003/login/authenticate", ok: false, …}
Everything works properly however, I get the error in the console. Like this:
I want the 400 bad request error to not appear in the console. How to do that?
login.component.ts
login(data) {
console.log("Inside Login");
this.authenticateObj = {
username: data.username,
password: data.password
}
this.http.post("http://localhost:3003/login/authenticate",
this.authenticateObj)
.map(Response => Response)
.catch((err) => {
console.log("err =", err)
alert('Login Failed. Username or Password
is incorrect');
return Observable.throw(err);
})
.subscribe((res: Response) => {
console.log("Inside login authenticate subscribe");
this.info = res;
if (this.info.message == 'Login Successful.') {
console.log("test after login = ", this.info);
if (localStorage.getItem('username') &&
localStorage.getItem('token')) {
alert('Login Successful');
this.router.navigate(['/file-upload/wsdl']);
} else {
this.notification.Error('Unauthorized');
}
}
if (this.info.message == 'error') {
alert('Login Failed');
}
else if (this.info.status == 400) {
alert('Login Failed');
}
})
}
login.controller.js
function authenticateUser(req, res, next) {
console.log("Inside authenticateUser = ", req.body)
LoginService.authenticate(req,req.body)
.then(function (token) {
if (token) {
res.setHeader("authorization",token.token);
res.send({
message: 'Login Successful.',
response: token
});
} else if(res.message == 'Username or Password is
incorrect'){
res.status(401).send({
message: 'Unauthorized. '
});
}
else {
console.log("inside controller, else res.status-400");
res.status(400).send({
message: 'Username or password is incorrect'
});
}
})
.catch(function (err) {
console.log("inside controller, catch res.status 400")
// res.status(400).send(err);
res.status(400).send({
message: 'Username or password is incorrect'
});
});
}
In order to handle errors from server properly, you have to catch them in the subcribe() method of the Observable returned by http.post from Rxjs:
this.http.post("http://localhost:3003/login/authenticate", this.authenticateObj)
.subscribe(
(res: Response) => {
// code when no error...
},
err => {
// error handling...
},
() => {
// finally...
}
);
IMO Bad request is an incorrect response by your server for incorrect username/password combination. You can return a "401" or a "200" itself depending on your requirement.
Now if you want the error not to appear in the console then add an error callback in your subscribe().
this.http.post("http://localhost:3003/login/authenticate", this.authenticateObj)
...
// rest of the code
.subscribe((res: Response) => {
// your code
}, (error) => {
// handle the error here, show some alerts, warnings, etc
console.log(error)
})

Unhandled promise rejection in express route

I am getting this error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent.
from this portion of my express route route:
router.post('/', jsonParser, (req, res) => {
// checking that given id is valid
let { id } = req.body;
User.findById({ id })
.count()
.then(count => {
if (count < 1) {
return Promise.reject({
code: 422,
reason: 'Validation Error',
message: 'Family must be created by a user',
location: 'id'
})
}
return resolve();
})
.catch(err => {
return res.status(err.code).json({code: err.code, message: err.message, reason: err.reason, location: err.location })
});
...
I'm not stellar at promises. Can someone see what I am doing incorrectly here?
resolve is undefined, you could use return count instead, to pass data to the next promise:
User.findById({ id })
.count()
.then(count => {
if (count < 1) {
throw new Error('Family must be created by a user')
}
// success
return count;
})
.then( res => { // res will be count if the promise not rejected
res.json({ success: true, count: res })
})
.catch(err => {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Family must be created by a user',
location: 'id'
})
});
.catch(err => { return res.status(err.code).json({code: err.code, message: err.message, reason: err.reason, location: err.location }) });
Here lies the issue. when a response is sent you can not send another response. So when you do
res.status(), express sends the response with status code. and .json() will give exception.
try setting status like this.
res.statusCode = code; and then
res.json()

Resources