How to see axios error response JSON in React - node.js

I have a form in my react which allows users fill out their info and then sends a Post requests to create a new user in my database once submitted. I am using axios to make the http call with a node backend. The post request works on the backend, but I am now trying to handle errors on the front end (i.e. if a user enters an invalid email, the backend catches this and sends an error status back, and I want to display a message to the user saying they need to enter a valid email, etc).
I have the following code on my backend to handle the request:
const { check, validationResult } = require('express-validator/check');
studentRoutes.route('/add').post([
check('email').isEmail(), check('class_year').isInt({min: 2014, max: 2022})],
function (req, res) {
//make sure input is valid
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
.
.
.
//post call succesful
res.status(200).send("Student successfully Added to database");
});
On the front end, I make the http call as follows:
app.post('student/add', obj)
.then(res => console.log(res.data))
.catch(err => console.log(err));
where app is axios.
When I try this with correct data (i.e. should not return error), the message "Student successfully added to database" prints to the console as expected.
When I try this with a bad email (i.e. should return error), all that is printed to my console is:
(X) POST http://localhost:4000/student/add 422 (Unprocessable Entity)
Error: Request failed with status code 422
at createError (createError.js:17)
at settle (settle.js:19)
at XMLHttpRequest.handleLoad (xhr.js:78)
Where the first POST error is caught by my browser and the second part starting "Error: ..." is printed to the console.
When I try to access the error (err.errors or similar) it returns undefined. How can I access the status code and the body of the error sent?
Note: I have also tried replacing the line return res.status(422).json({ errors: errors.array() }); with return res.status(422).send({ errors: errors.array() }); but that does not help and the results are the same.

You should be able to access like this:
catch (err) {
console.error(err.response.data.errors);
}

Try:
error.message
Or depending on the backend config:
error.response

Related

How to read 400 or 500 JSON response [duplicate]

This question already has answers here:
How to catch the server response, even if 400 bad request error occured?
(2 answers)
Closed 12 months ago.
I have a Nodejs server running that registers the users. On successful registration, it returns a status code of 200 and a JSON response. But on unsuccessful registration where the email already exists, it returns the status code of 400 and a JSON response.Now the problem is that I'm able to read the JSON of 200 responses but I'm not able to access the JSON of 400 responses. I am using axios to fetch the data from the server.
Code for the email that already exists
if (emailExists) {
return res.status(400).json({
success: false,
message: 'This email address is already being used.'
})
}
Response for a valid registration
return res.status(200).json({
success: true,
message: 'You are signed up successfully.',
})
Response on successful signup
Behaviour when the email is already being used
This is how I'm sending the request
With axios(), to handle a 400 error, you need to use a .catch() to catch the rejected promise and then examine the error object you get. So, in addition to your .then(), use a .catch():
axios(...).then((result) => {
console.log(result);
}).catch(err => {
console.log(err.response.status);
console.log(err.response.data);
});
Or, if you were using an await fetch(...), then surround that with a try/catch and catch the error in the catch block.
In your code ALL promises that can ever reject MUST have an error handler for the rejection. Since your console shows "uncaught (in promise) Error", that means you had no means of catching the rejected promise.
You can intercept the response before they are handled by then or catch. Example below:
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
Doc reference: https://axios-http.com/docs/interceptors

Why is NodeJS crashing when API returns 400 HTTP status code?

I was working with this API and everything was fine until I've tried POST method.
Now what happens is, when I try to send data from postman, NodeJS app crashes, displaying this error: HTTPError: Response code 400 (Bad Request) and pointing that error is called in the catch part.
So thing is, when I directly send post request to the API directly (not with my localhost server) it returns 400: Bad request with message that data is not correct. Now everything is okay when I send it directly, but when I try to call the API with my localhost it throws that error.
The error is throw from the call: got.post(...) which uses this library:
https://www.npmjs.com/package/got
Is it possible to make that call and get the 400 response without getting an error raised from the call?
methods.ts
export const addReservation = async (postData: object) => {
const url = API_URL + '/reservation';
try {
const response = await got.post(url, {
json: { ...postData },
responseType: 'json',
});
console.log(response.statusCode); //no success, jumps to the catch block
return response.body;
} catch (error) {
throw new Error(error);
}
};
reservation.ts
export const reservationHandler = async (req: Request, res: Response) => {
let postData = req.body;
let response = await addReservation(postData);
return res.status(201).json(response); //if all is OK it will return 201, but if not 400
};
So I've tried catching the status code in addReservation but no success. I was having idea by handling cases of status codes 400 or 201, but only proceeds with try block if the status is 201, but if it is 400, it crashes the app.
You have to set throwHttpErrors to false in got options.
https://github.com/sindresorhus/got#throwhttperrors

Client (react) cannot access response from Server (express)

I'm working on a post route for an express.js server. I've got a validation function that if not passed, returns a 400 code with message. The client (React), receives the 400 code, but I cannot access the message sent by the server.
This is within the POST in express:
const { error } = validate(req.body);
if (error)
return res
.status(400)
.send(error.details[0].message);
This is the submit routine in React:
doSubmit = async () => {
const { username, password } = this.state.data;
const result = await login({ mail: username, password });
console.log('result', result);
};
Login is only a wrapper for axios.post method:
export async function login(user) {
try {
return await http.post(apiEndpoint, user);
} catch (ex) {
console.log(ex.message);
return ex;
}
}
Currently, the code works as it is. Submitting an invalid input, such as a password below 5 characters, i get the following in the client:
POST http://localhost:3000/api/auth 400 (Bad Request)
Request failed with status code 400
result Error: Request failed with status code 400
at createError (createError.js:17)
at settle (settle.js:19)
at XMLHttpRequest.handleLoad (xhr.js:78)
at XMLHttpRequest.wrapped (raven.js:363)
However, if I change the status code sent by the server to "200", I get:
result {
full response here along with data property..
data: ""password" length must be at least 5 characters long"
}
I've been reading the Express documentation. It doesn't say anything about not sending the response if the code is 400 (although it makes sense to send nothing if bad request). I've also toyed a bit with 'body-parser' but didn't get different results.
However, if I test in postman, it works like a charm! With same input, i get the status code and the message.
Finally, it's ok to validate both the input sent (client) and the data received(server) right? If so, let's say the client is OK with the input, but the server is not. Should the server respond with whatever the problem is (what i'm trying to achieve), or simply say 'ERROR' (current scenario)?
Thank you.
Axios code example, may be given code fix your issue. The issue is with your parameters.
axios({
url: '/login',
method: 'post',
params: { username: 'cedas', password: 'fredsed' }
}).then(response => {
resolve(response)
})
.catch(error => {
reject(error)
});
})
Well, in the end, the problem was accessing ex.message, instead of ex.response ... Cheers.

Error: Can't set headers after they are sent. - NodeJS and Express

I have a NodeJS Rest API where I have a user collection, besides that I do user SMS verification.
This is the controller for the POST /:id/verification
exports.verification = (req, res) => {
const id = req.params.id
return User.find(id)
.then( user => {
if (user.code !== req.body.code) {
res.json({ message: 'Incorrect code' })
res.sendStatus(500)
return
}
user.isVerified = true
user.save( error => {
if (error) {
res.json({ message: 'Failed to update user' })
res.sendStatus(500)
return
}
res.json({ user })
res.sendStatus(200)
} )
} )
.catch( error => {
res.json({ error })
} )
}
But the thing is that when I post to /:id/verification I get this error
Error: Can't set headers after they are sent. - NodeJS and Express
On this line:
res.json({ user })
res.sendStatus(200)
But I dont understand why, I dont send any response before this.
Can someone explain me what Im doing wrong?
you are using both res.json() and res.sendStatus() both together, both of them send response back, That is why it is showing error that Can't set headers after they are sent.
you should use only one of them.
If you want to send status along with the JSON response, you can try this:
res.status(500).json({ message: 'Incorrect code' });
Also, status of 200 is default when using res.send, res.json, etc. So you dont need to send status 200 with res.json()
res.json() send object to the clilent and after that you are trying to set the header with status code. So, it shows the error message. Use following code for set status and sending the content in the same time.
res.status(500).json({ error: 'message' } /* json object*/);

Proper way to set response status and JSON content in a REST API made with nodejs and express

I am playing around with Nodejs and express by building a small rest API. My question is, what is the good practice/best way to set the code status, as well as the response data?
Let me explain with a little bit of code (I will not put the node and express code necessary to start the server, just the router methods that are concerned):
router.get('/users/:id', function(req, res, next) {
var user = users.getUserById(req.params.id);
res.json(user);
});
exports.getUserById = function(id) {
for (var i = 0; i < users.length; i++) {
if (users[i].id == id) return users[i];
}
};
The code below works perfectly, and when sending a request with Postman, I get the following result:
As you can see, the status shows 200, which is OK. But is this the best way to do this? Is there a case where I should have to set the status myself, as well as the returned JSON? Or is that always handled by express?
For example, I just made a quick test and slightly modified the get method above:
router.get('/users/:id', function(req, res, next) {
var user = users.getUserById(req.params.id);
if (user == null || user == 'undefined') {
res.status(404);
}
res.json(user);
});
As you can see, if the user is not found in the array, I will just set a status of 404.
Resources/advices to learn more about this topic are more than welcome.
Express API reference covers this case.
See status and send.
In short, you just have to call the status method before calling json or send:
res.status(500).send({ error: "boo:(" });
You could do it this way:
res.status(400).json(json_response);
This will set the HTTP status code to 400, it works even in express 4.
status of 200 will be the default when using res.send, res.json, etc.
You can set the status like res.status(500).json({ error: 'something is wrong' });
Often I'll do something like...
router.get('/something', function(req, res, next) {
// Some stuff here
if(err) {
res.status(500);
return next(err);
}
// More stuff here
});
Then have my error middleware send the response, and do anything else I need to do when there is an error.
Additionally: res.sendStatus(status) has been added as of version 4.9.0
http://expressjs.com/4x/api.html#res.sendStatus
A list of HTTP Status Codes
The good-practice regarding status response is to, predictably, send the proper HTTP status code depending on the error (4xx for client errors, 5xx for server errors), regarding the actual JSON response there's no "bible" but a good idea could be to send (again) the status and data as 2 different properties of the root object in a successful response (this way you are giving the client the chance to capture the status from the HTTP headers and the payload itself) and a 3rd property explaining the error in a human-understandable way in the case of an error.
Stripe's API behaves similarly in the real world.
i.e.
OK
200, {status: 200, data: [...]}
Error
400, {status: 400, data: null, message: "You must send foo and bar to baz..."}
I am using this in my Express.js application:
app.get('/', function (req, res) {
res.status(200).json({
message: 'Welcome to the project-name api'
});
});
The standard way to get full HttpResponse that includes following properties
body //contains your data
headers
ok
status
statusText
type
url
On backend, do this
router.post('/signup', (req, res, next) => {
// res object have its own statusMessage property so utilize this
res.statusText = 'Your have signed-up succesfully'
return res.status(200).send('You are doing a great job')
})
On Frontend e.g. in Angular, just do:
let url = `http://example.com/signup`
this.http.post(url, { profile: data }, {
observe: 'response' // remember to add this, you'll get pure HttpResponse
}).subscribe(response => {
console.log(response)
})
res.status(500).jsonp(dataRes);
try {
var data = {foo: "bar"};
res.json(JSON.stringify(data));
}
catch (e) {
res.status(500).json(JSON.stringify(e));
}
The best way of sending an error response would be return res.status(400).send({ message: 'An error has occurred' }).
Then, in your frontend you can catch it using something like this:
url: your_url,
method: 'POST',
headers: headers,
data: JSON.stringify(body),
})
.then((res) => {
console.log('success', res);
})
.catch((err) => {
err.response && err.response.data && this.setState({ apiResponse: err.response.data })
})
Just logging err won't work, as your sent message object resides in err.response.data.
Hope that helps!
You could do this
return res.status(201).json({
statusCode: req.statusCode,
method: req.method,
message: 'Question has been added'
});
FOR IIS
If you are using iisnode to run nodejs through IIS, keep in mind that IIS by default replaces any error message you send.
This means that if you send res.status(401).json({message: "Incorrect authorization token"}) You would get back You do not have permission to view this directory or page.
This behavior can be turned off by using adding the following code to your web.config file under <system.webServer> (source):
<httpErrors existingResponse="PassThrough" />
res.sendStatus(status) has been added as of version 4.9.0
you can use one of these res.sendStatus() || res.status() methods
below is difference in between res.sendStatus() || res.status()
res.sendStatus(200) // equivalent to res.status(200).send('OK')
res.sendStatus(403) // equivalent to res.status(403).send('Forbidden')
res.sendStatus(404) // equivalent to res.status(404).send('Not Found')
res.sendStatus(500) // equivalent to res.status(500).send('Internal Server Error')
I hope someone finds this helpful
thanks
I don't see anyone mentioned the fact that the order of method calls on res object is important.
I'm new to nodejs and didn't realize at first that res.json() does more than just setting the body of the response. It actually tries to infer the response status as well. So, if done like so:
res.json({"message": "Bad parameters"})
res.status(400)
The second line would be of no use, because based on the correctly built json express/nodejs will already infer the success status(200).

Resources