Vuex axios call can't handle 422 response - javascript

I'm trying to handle 422 response from my API in case data is invalid when making an async Axios call.
In a component, I have a method like:
async saveChanges() {
this.isSaving = true;
await this.$store.dispatch('updateSettings', this.formData);
this.isSaving = false;
}
And in my actions like this:
let response;
try {
response = await axios.put('/api/settings', settings);
console.log(response);
} catch (e) {
console.log('error');
console.log(e);
}
If the request is successful, everything works fine, and I get a response. However, if the response status code is 422, it doesn't fail or throw an exception, and the response is empty. I've tried with .then(()).catch(()), but no luck either as I need it to be async.
Any suggestions what I might be doing wrong?

By default, Axios only throws an error for HTTP status codes between 200 and 300.
Either configure Axios to also throw for 422 by setting validateStatus:
axios.put('/api/settings', settings, {
validateStatus: status => status >= 200 && status < 300 || status === 422
})
Or check the status code in your normal callback:
response = await axios.put('/api/settings', settings);
if (response.status === 422) {
// throw error
}

Related

Why I'm not being able to apply the .json() method to a Response object when the Response status is other than 200?

I have the following function
async function favoriteGatos() {
const response = await fetch(API_URL_FAVOURITES);
console.log(response);
const data = await response.json();
console.log(data)
if (response.status !== 200) {
spanError.innerHTML = "Hubo un error: " + response.status + data.message;
} else {}
}
When everything is going well and the http status for the fetch response is 200 the object 'data' will be created with no problem, but when something goes wrong, for exaple the API key is wrong, creating a response with a http status code different from 200, I can no longer apply the .json() method to the response and the code breaks at that point showing the following message on the console
Uncaught (in promise) SyntaxError: Unexpected token 'A', "AUTHENTICA"... is not valid JSON
I can no longer apply the .json() method
Because the response body isn't JSON data. It's likely just text, so you'd use response.text() instead.
As to why the server is returning different data formats depending on the HTTP code, that's a debate to have with whoever maintains that server. But the observation from your end is that an HTTP 200 (or similar) returns JSON, whereas any error codes return text.
In that case you'd have to check the response code before trying to deserialize the data:
async function favoriteGatos() {
const response = await fetch(API_URL_FAVOURITES);
if (response.status !== 200) {
const data = await response.json();
spanError.innerHTML = "Hubo un error: " + response.status + data.message;
} else {
const data = await response.text();
// handle the error response in some way
}
}

Why Request API when error always goes to catch

I have some questions about requesting API from the server. I make a function for request API, the example I have a request API login when the user fills the email wrong, response API is "email or password is wrong!", when I try in postman is success the response but when I try in my code the response always from the catch, not from response API. My code for request API like below
const handleSubmitLogin = async (input) => {
try {
const result = await axios.post(`${BASE_URL}/users/client/login`, input);
if (result.status == 200 || result.status === "success" || result.status == 201) {
await setAuthKey(result.data.data.token);
await setLoggedUser(JSON.stringify(result.data.data));
dispatch(setUserLogin());
dispatch(setDataLogin(result.data.data));
} else {
setModalActive({ status: true, data: result.message });
}
} catch (error) {
console.log(error);
setModalActive({ status: true, data: translations["please.try.again"] });
}
};
when a user fills an email or password wrong, the response is always from the catch response not from the API response. Can anyone give suggestions for this case?
Edit:
This is result from when user wrong password
If your API responds with a non-successful status code (>= 400), Axios will reject the promise and your code will go into the catch block.
You can still access the response data via error.response.data. See Axios - Handling Errors
try {
const result = await axios.post(`${BASE_URL}/users/client/login`, input);
// etc...
} catch (err) {
console.warn("login", error.toJSON());
setModalActive({
status: true,
data: error.response?.data?.message ?? translations["please.try.again"],
});
}
It's important to use optional chaining since the error may not have a response or data depending on what exactly failed.

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

Interceptor for fetch and fetch retry? (Javascript)

I am trying to create an interceptor for fetch in javascript (React to be more specific). It should get the result from every fetch that gets called, and if it is an 401 error it should initiate a new fetch call to another route to get a cookie (a refresh token). Then, the original fetch call should be tried again (because now the user is logged in).
I have managed to trigger the new fetch call and send back the cookie for each, but I got these two problems below:
I do not now how to retry the fetch call after the refresh token has been recieved. Is that possible? I found the fetch-retry npm (https://www.npmjs.com/package/fetch-retry) but not sure how and if I can implement that on an interceptor when it should be done for the original fetch call.
I seem to be doing something wrong with async await (I think), because the intercept is not waiting for the fetch call before returning the data (the statuscode on the original fetch seems to be 401 and not 200 which it should be after we get the cookie. I also tried to return the response of the fetch inside the interceptor but that returned undefined).
Any idea about how to solve this? Anyone who have done something similar?
Below is my code:
(function () {
const originalFetch = fetch;
fetch = function() {
return originalFetch.apply(this, arguments).then(function(data) {
if(data.status === 401) {
console.log('not authorized, trying to get refresh cookie..')
const fetchIt = async () => {
let response = await fetch(`/api/token`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
});
}
fetchIt();
}
return data
});
};
})();
EDIT: To make it more clear what I am after. I need an interceptor like I described above to work so I don't have to do something like this after every fetch call:
getData() {
const getDataAsync = async () => {
let response = await fetch(`/api/loadData`, { method: 'POST' });
if(response.status === 401) {
let responseT = await fetch(`/api/token`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
});
if(responseT.status === 401) {
return responseT.status
}
if(responseT.status === 200) {
response = await fetch(`/api/loadData`, { method: 'POST' });
}
}
let data = await response.json();
//Do things with data
};
getDataAsync();
};
So basically the interceptor should:
Check if there is a 401, if so then:
fetch api/token
If api/token returns 401, it should just return that.
If api/token returns 200, it should run original fetch again
You can simple use originalFetch for token and await for response if response is 401 then you simply return empty response to first fetch call else you updated token and then let it go to next condition which will rerun old request.
let TEMP_API = {
'401': {
url: 'https://run.mocky.io/v3/7a98985c-1e59-4bfb-87dd-117307b6196c',
args: {}
},
'200': {
url: 'https://jsonplaceholder.typicode.com/todos/2',
args: {}
},
'404': {
url: 'https://jsonplaceholder.typicode.com/todos/1',
args: {
method: "POST",
credentials: "include"
}
}
}
const originalFetch = fetch;
fetch = function() {
let self = this;
let args = arguments;
return originalFetch.apply(self, args).then(async function(data) {
if (data.status === 200) console.log("---------Status 200----------");
if (data.status === 401) {
// request for token with original fetch if status is 401
console.log('failed');
let response = await originalFetch(TEMP_API['200'].url, TEMP_API['200'].args);
// if status is 401 from token api return empty response to close recursion
console.log("==========401 UnAuthorize.=============");
console.log(response);
if (response.status === 401) {
return {};
}
// else set token
// recall old fetch
// here i used 200 because 401 or 404 old response will cause it to rerun
// return fetch(...args); <- change to this for real scenarios
// return fetch(args[0], args[1]); <- or to this for real sceaerios
return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
}
// condition will be tested again after 401 condition and will be ran with old args
if (data.status === 404) {
console.log("==========404 Not Found.=============");
// here i used 200 because 401 or 404 old response will cause it to rerun
// return fetch(...args); <- change to this for real scenarios
// return fetch(args[0], args[1]); <- or to this for real scenarios
return fetch(TEMP_API['200'].url, TEMP_API['200'].args);
sceaerios
} else {
return data;
}
});
};
(async function() {
console.log("==========Example1=============");
let example1 = await fetch(TEMP_API['404'].url, TEMP_API['404'].args);
console.log(example1);
console.log("==========Example2=============");
let example2 = await fetch(TEMP_API['200'].url, TEMP_API['200'].args);
console.log(example2);
console.log("==========Example3=============");
let example3 = await fetch(TEMP_API['401'].url, TEMP_API['401'].args);
console.log(example3);
})();
Example1 request made to api for 404 status which will cause the 404 condition to run which will then call 200 api after which response will be returned
Example2 request made to 200 api which will return 200 status code which will cause 200 condition to pass and run and return response
Example3 request made to api for 401 status which will cause 401 condition to pass which will then call 200 api and print response after which it will fall out of condition where you can set token which will then be used in another fetch request
Try retuning the fetch promise instead of awaiting that.
(function () {
const originalFetch = fetch;
fetch = function () {
return originalFetch.apply(this, arguments).then(function (data) {
if (data.status === 200) console.log("---------Status 200----------");
if (data.status === 404) {
console.log("==========404 Not Found.=============");
return fetch(`https://jsonplaceholder.typicode.com/todos/2`);
} else {
return data;
}
});
};
})();
function test(id) {
//will trigger 404 status
return fetch(`https://jsonplaceholder.typicode.com/todos/` + id, {
method: "POST",
credentials: "include",
});
}
test(1).then((i) => console.log(i));
Interceptor library for the native fetch command. It patches the global fetch method and allows you the usage in Browser, Node and Webworker environments.
fetch-retry It wraps any Fetch API package (eg: isomorphic-fetch, cross-fetch, isomorphic-unfetch and etc.) and retries requests that fail due to network issues. It can also be configured to retry requests on specific HTTP status codes.

Why axios doesnt return API response when error thrown?

Im using React with redux-saga.
I have a simple /GET request. However, if any error is thrown by API, e.g. 400 bad request, Im unable to get the response returned by my API.
Example - /getUsers returns 400 bad request and a response "Your form is invalid". I can see it in network, that it was properly returned. However, in my try catch:
catch((error) => console.log(error.message))
is always a Network Error. I dont want to display to user Network Error, but the response returned by API.
Question:
How can I get correct API response error message?
request function:
const getUsers = () => api.get(URL);
axios instance
const api = axios.create();
saga function (just an example)
try {
yield call(getUsers);
} catch(error) {
yield put(getUsersFail(error.message));
}
We have to get the response from axios call at first and check it's status. If it's a 400 error throw and catches it.
try {
const response = yield call(api.get, url)
if (response.status >= 200 && response.status < 300) {
yield put({ type.TYPE, response })
} else {
throw response
}
} catch(error) {
// API response error message
yield put({type.TYPE_ERROR, error.message})
}
try this please:
try {
const response = yield call(api.get, url)
if (response.status >= 200 && response.status < 300) {
yield put({ type.TYPE, response })
}
else {
throw new Error(JSON.stringify(response));
}
} catch(error) {
const errorMessage = error.response && error.response.error && error.response.error.message;
const errorMessage2 = error.request;
const errorMessage3 = error.message;
const reportedError = errorMessage || errorMessage2 || errorMessage3;
yield put({type.TYPE_ERROR, reportedError })
}

Categories

Resources