How to catch and handle error response 422 with Redux/Axios? - javascript

I have an action making a POST request to the server in order to update a user's password, but I'm unable to handle the error in the chained catch block.
return axios({
method: 'post',
data: {
password: currentPassword,
new_password: newPassword
},
url: `path/to/endpoint`
})
.then(response => {
dispatch(PasswordUpdateSuccess(response))
})
.catch(error => {
console.log('ERROR', error)
switch (error.type) {
case 'password_invalid':
dispatch(PasswordUpdateFailure('Incorrect current password'))
break
case 'invalid_attributes':
dispatch(PasswordUpdateFailure('Fields must not be blank'))
break
}
})
When I log the error this is what I see:
When I check the network tab I can see the response body, but for some reason I can't access the values!
Have I unknowingly made a mistake somewhere? Because I'm handling other errors from different request fine, but can't seem to work this one out.

Example
getUserList() {
return axios.get('/users')
.then(response => response.data)
.catch(error => {
if (error.response) {
console.log(error.response);
}
});
}
Check the error object for response, it will include the object you're looking for so you can do error.response.status
https://github.com/mzabriskie/axios#handling-errors

Axios is probably parsing the response. I access the error like this in my code:
axios({
method: 'post',
responseType: 'json',
url: `${SERVER_URL}/token`,
data: {
idToken,
userEmail
}
})
.then(response => {
dispatch(something(response));
})
.catch(error => {
dispatch({ type: AUTH_FAILED });
dispatch({ type: ERROR, payload: error.data.error.message });
});
From the docs:
The response for a request contains the following information.
{
// `data` is the response that was provided by the server
data: {},
// `status` is the HTTP status code from the server response
status: 200,
// `statusText` is the HTTP status message from the server response
statusText: 'OK',
// `headers` the headers that the server responded with
headers: {},
// `config` is the config that was provided to `axios` for the request
config: {}
}
So the catch(error => ) is actually just catch(response => )
EDIT:
I still dont understand why logging the error returns that stack message. I tried logging it like this. And then you can actually see that it is an object.
console.log('errorType', typeof error);
console.log('error', Object.assign({}, error));
EDIT2:
After some more looking around this is what you are trying to print. Which is a Javascipt error object. Axios then enhances this error with the config, code and reponse like this.
console.log('error', error);
console.log('errorType', typeof error);
console.log('error', Object.assign({}, error));
console.log('getOwnPropertyNames', Object.getOwnPropertyNames(error));
console.log('stackProperty', Object.getOwnPropertyDescriptor(error, 'stack'));
console.log('messageProperty', Object.getOwnPropertyDescriptor(error, 'message'));
console.log('stackEnumerable', error.propertyIsEnumerable('stack'));
console.log('messageEnumerable', error.propertyIsEnumerable('message'));

Here is the proper way to handle the error object:
axios.put(this.apiBaseEndpoint + '/' + id, input)
.then((response) => {
// Success
})
.catch((error) => {
// Error
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
// console.log(error.response.data);
// console.log(error.response.status);
// console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
Origin url https://gist.github.com/fgilio/230ccd514e9381fafa51608fcf137253

axios.post('http://localhost:8000/api/auth/register', {
username : 'test'
}).then(result => {
console.log(result.data)
}).catch(err => {
console.log(err.response.data)
})
add in catch
geting error response ==> err.response.data

I was also stumped on this for a while. I won't rehash things too much, but I thought it would be helpful to others to add my 2 cents.
The error in the code above is of type Error. What happens is the toString method is called on the error object because you are trying to print something to the console. This is implicit, a result of writing to the console. If you look at the code of toString on the error object.
Error.prototype.toString = function() {
'use strict';
var obj = Object(this);
if (obj !== this) {
throw new TypeError();
}
var name = this.name;
name = (name === undefined) ? 'Error' : String(name);
var msg = this.message;
msg = (msg === undefined) ? '' : String(msg);
if (name === '') {
return msg;
}
if (msg === '') {
return name;
}
return name + ': ' + msg;
};
So you can see above it uses the internals to build up the string to output to the console.
There are great docs on this on mozilla.

The only thing what helped me was the following:
axios.put('/api/settings', settings, {
validateStatus: status => status >= 200 && status < 300 || status === 422
})
https://stackoverflow.com/a/66285529/5849569

You can use inline if else statement like so:
.catch(error => {
dispatch({
type: authActions.AUTH_PROCESS_ERROR,
error: error.response ? error.response.data.code.toString() : 'Something went wrong, please try again.'
});
});

I recommend handling errors via Axios interceptors, individually for each case scenario:
// interceptor to catch errors
const errorInterceptor = (error) => {
// check if it's a server error
if (!error.response) {
console.log('📡 API | Network/Server error')
return Promise.reject(error)
}
// all the error responses
switch (error.response.status) {
case 400:
console.error(error.response.status, error.message)
console.log('📡 API | Nothing to display', 'Data Not Found')
break
case 401: // authentication error, logout the user
console.log('📡 API | Please login again', 'Session Expired')
localStorage.removeItem('user')
break
case 403:
console.error(error.response.status, error.message)
console.log('📡 API | Access denied', 'Data Not Found')
break
case 404:
console.error(error.response.status, error.message)
console.log('📡 API | Dataset not found', 'Data Not Found')
break
case 422:
console.error(error.response.status, error.message, error.response.data.detail)
console.log('📡 API | Validation error', 'Unprocessable Content')
break
default:
console.error(error.response.status, error.message)
}
return Promise.reject(error)
}

Related

Trying to access error response 500 axios

I am not able to access the response of error - 500 in axios
export const dowloadFilePDF = (data) => {
return axios
.request({
method: 'GET',
url: `${basePath + data[0]}`,
responseType: 'blob',
headers: { Authorization: Authorization },
})
.then(response => {
console.log(response)
let fileName = response.headers['content-disposition']?.split(';')[1]?.split('=')[1]?.split('"').join('')
fileName = fileName ? fileName : 'data.pdf'
fileDownload(response.data, fileName)
})
.catch((error) => {
console.log(error.response.data)
})
}
I am not getting the response instead its returning as
data : Blob {size: 215, type: 'application/json'}
According to the documentation, you can't assume error.response will be filled in. Here's the code the documentation shows with the inline comments explaining it:
Handling Errors
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
There's another aspect to this as well: You're calling catch on the promise returned by then, not on the promise returned by axios. If the axios promise is rejected, you'll reach that rejection handler, but you'll also reach it if the axios promise is fulfilled but then your fulfillment handler throws an error (or returns a promise it ultimately rejects). In that latter case, the error probably won't have a response property at all.
the best way to catch errors instead of trying a lot of lines of code in the catch method by promise is using the tools in the Axios names interceptor.
interceptor has two property request and response. In response we can simulate the errors status and based on the status code we can do whatever we want. for example :
axios.interceptors.response.use(null, error => {
console.log("error : " , error);
const expectedError = error.response && error.response.status >= 400 &&
error.response.status < 500;
if (expectedError) {
return Promise.reject(error);
}
alert("unexpected error is happen");
});
if you need more help here is the original link

Firebase 'Unhandled Rejection' and 'Can't set headers after they are set' JavaScript error

Firstly, please note that I am very very new to JS and coding as a general :)
Desired behaviour:
I have written the following JS HTTPS Firebase function which, which takes in a query parameter locationId, it performs a GET API call and saves the response back to Firebase. The code correctly saves the data to Firebase as desired. I have come across similar issues but i'm struggling to adapt those solutions to my specific issue below. From what I see, I'm only sending the response once.
Specific error: The following is the console output
Cannot set headers after they are sent to the client
Unhandled rejection
My function:
exports.doshiiGetMenuForOnboardedVenue = functions.https.onRequest((req, res) => {
// Forbidding PUT requests.
if (req.method === 'PUT') {
return res.status(403).send('Forbidden!');
}
cors(req, res, () => {
const locationId = req.query.locationId;
console.log('locationId', locationId);
if (locationId){
console.log('locationId', locationId);
var token = jwttoken();
const options = {
headers: {
'content-type': 'application/json',
'authorization': 'Bearer ' + token}
};
const uri = 'https://sandbox.doshii.co/partner/v3/locations/' + locationId + '/menu?lastVersion=:lastVersion&filtered=true'
axios.get(uri, options)
.then(response => {
return admin.database().ref(`/venue-menus/${locationId}/`).set(response.data)
.then(response => {
return res.status(200).send(locationId)
})
.catch(err => {
return res.status(500).send({error: err})
})
})
.then(response => {
return res.status(200).send(locationId)
})
.catch(err => {
return res.status(500).send({error: err})
})//end axios
} else {
return res.status(500).send({error: 'locationId missing'})
}//end if-else (!locationId)
})//end cors
});
By flattening out your nested promises, you can see that your code is performing the following instructions (when the axios call doesn't throw an error):
admin.database().ref(`/venue-menus/${locationId}/`).set(response.data))
.then(response => res.status(200).send(locationId))
.catch(err => res.status(500).send({error: err})
.then(response => res.status(200).send(locationId)) // this line is always called after either of the above.
.catch(err => res.status(500).send({error: err})
As general practice, unless required, you should not nest promises with their own then() and catch() handlers as it will lead to bizarre effects like this.
Furthermore, if your code calls for using //end axios or //end cors messages, you should flatten out your code so it makes sense without those messages.
Adapting your code to "fail fast", correcting your API responses and appropriately hiding error stack traces gives:
const cors = require('cors')({
origin: true,
methods: ["GET"]
});
exports.doshiiGetMenuForOnboardedVenue = functions.https.onRequest((req, res) => {
cors(req, res, (err) => { // note: cors will handle OPTIONS method
if (err) {
// note: log full error at ERROR message level
console.error('Internal CORS error:', err);
// note: return only generic status message to client
return res.status(500).json({error: 'Internal Server Error'});
}
// Forbidding anything but GET requests.
if (req.method !== 'GET') {
// 405 METHOD_NOT_ALLOWED
return res.status(405)
.set('Allow', 'GET')
.json({error: 'Not Allowed!'});
}
const locationId = req.query.locationId;
console.log('locationId', locationId);
if (!locationId) {
// 400 BAD_REQUEST
return res.status(400).json({error: 'locationId missing'})
}
var token = jwttoken();
const options = {
headers: {
'content-type': 'application/json',
'authorization': 'Bearer ' + token
}
};
// note: Don't forget to enable billing for third-party APIs!
const uri = 'https://sandbox.doshii.co/partner/v3/locations/' + locationId + '/menu?lastVersion=:lastVersion&filtered=true'
axios.get(uri, options)
.then(response => admin.database().ref(`/venue-menus/${locationId}/`).set(response.data))
.then(() => {
// note: as locationId was already sent by the client, send new/useful
// information back or nothing but the right status code
res.status(200).json({ ref: `/venue-menus/${locationId}/` });
})
.catch(err => {
// note: log full error at ERROR message level
console.error('Failed to retrieve/save API data:', err);
// note: return only message to client
res.status(500).json({error: err.message || 'Internal Server Error'});
});
});
});

What's the best way to deal with an error in the server side and in the client side using nodejs + express

I'd like to know the best way to deal with errors in a response - request.
I have this route that receive a request:
app.get('/getInfo', function (req, res, next) {
let obj = {}
try {
obj = {
...
date: lastUpdatedDate('./utils/appVersion.js'),
...
}
res.status(200).send(obj)
} catch (error) {
console.log(error.message)
res.send({error: "The data wasn't load"})
}
})
And this function where the request is made
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
console.log(error)
})
}
What's the best way to deal with the error if it occurs in the server side?
Let's supose that in this code block the directory doesn't exists: lastUpdatedDate('./directoreyDoesntExists/appVersion.js'),
So my code goes to the catch block.
Should I send the error like this:
res.send({error: "The data wasn't load"})
Should I set a status like this?
res.status(500).send({error: "The data wasn't load"})
Or should I set a status with a different status code?
Based on that, what's the best way to deal with it in my frontend method getInfo() to get the error and show the error message on web interface?
Should I do an if else inside the .then block like this?
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
Or should I deal with this error directly in the catch block like this
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
For this case
res.send({error: "The data wasn't load"})
vs
res.status(500).send({error: "The data wasn't load"})
send a status is just more detailed, but both are ok.
check Proper way to set response status and JSON content
For this case, depends on what you need
then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
vs
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
You can handle all the errors sending them to the catch block
else if (resp.status === 400){
//print error message on web interface
not printing the error in here but throwing a new error that will be send it to the catch block
throw new ApiError("UserNotFount",400,"not found");
throw new Error('Error 400, not found');
For this case
res.send({error: "The data wasn't load"})
vs
res.status(500).send({error: "The data wasn't load"})
I would suggest sending error as well as status code because that will be more descriptive for the client.
and for the second case
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
vs
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
In this case I would suggest to use the catch block directly whenever you get an error because response status depends on error but not the other way around
As a beginner working on a REST Api, you should take a look at a guidelines - microsoft's are pretty legit: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design.
Basically, you need to return the correct HTTP code for each request, take a look at https://http.cat/ - for example if the request is malformed, return 400, and if the user is unauthorized return 401:
if (!req.body.name) {
res.status(400).send({ error: 'missing user name' }); // 400 bad request
}
const user = getUser(req.body.name, req.body.pass);
if(!user) {
res.status(401).send({ error: 'user does not exist' }); // 401 unauthorized
}
try {
const token = createToken(user);
// better to set a cookie
res.status(200).send({ token }); // 200 success
} catch(e) {
res.status(500).send({ erroe: e.message }); // 500 internal error
}
if(isTeapot) {
res.status(418).send({ error: 'I can only make tea' }); // 418 teapot, totally real
}
To make things easier there are a lot of libraries to help you generate better error messages and handle errors better, one of my favorites is celebrate
Any status code other that 200 would mean unsuccessful so you dont need to use those if-else statements. The better alternative is to catch the error and send it with response as it is. The benefit is that you would receive the type of error occured without hardcoding the status codes.
(for ex, we take the status code here to be 400 unsuccessful)
.catch(function (error) {
//print error message on web interface
res.status(400).send(JSON.stringify(error, undefined, 2));
});
By using the stringify method you can print the exact error on the console also.
.catch(function (error) {
console.log(JSON.stringify(error, undefined, 2));
});
The parameters in the stringify method here are:
error object
undefined: The array which contains the keys for filtering the keys in the object(here, error). All those keys present in this array are only the ones not filtered out.
2: It is used to introduce whitespace in object representation

How to check status in response from HTTP call

I am trying to navigate to next page if status code is 200 or response is true. But getting undefined in response.status. If I just log only response I get
{success: true, message: "login successful"}
This below code is getting undefined in console log. I am beginner to Angular and not using any service.
goToHome() {
console.log(this.login);
this.Http.post('http://localhost:3000/users/login', this.login).subscribe((response: Response) => {
console.log(response.status);
// if (resp.status === 200) {
// this.navCtrl.push(TabsPage);
// } else {
// const toast = this.toastController.create({
// message: 'User not found please try again',
// duration: 2000
// });
// toast.present();
// }
})
}
You should add options to your request, and set observe: 'response' to read full response.
this.Http.post('http://localhost:3000/users/login', this.login, { observe: 'response' })
.subscribe((response) => {
console.log(response.status);
})
More info in documentation: Reading the full response
You should use observe option ({observe: 'response'}) to get a full response that includes the response status code:
goToHome() {
this.Http.post('http://localhost:3000/users/login', this.login, {observe: 'response'})
.subscribe((response) => {
console.log(response.status);
})
}

Get a JSON response from an API?

I'm posting to an api where a failed response is output on purpose:
return response()->json([
'message' => 'Record not found',
], 422);
In Chrome dev tools I can see I get a 422 response with the response of {"message":"Record not found"}
In javascript I wish to get the message and log it, but i'm struggling to do so, here's my javascript:
axios.post('/api/test', {
name: 'test'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error) //this is Error: Request failed with status code 422
console.log(error.message); //this is blank
});
How can I get the message?
I found a code that can help you understand the catch block very well here :
axios.post('/api/test', {
name: 'test'
})
.then((response) => {
// Success
})
.catch((error) => {
// Error
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
// console.log(error.response.data);
// console.log(error.response.status);
// console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
try to catch like this:
.catch(function(error){
console.log(error);
if (error.response) {
console.log(error.response.data.message);
}
});

Categories

Resources