How to get axios error response INTO the redux saga catch method - axios

With axios the code is:
export const createBlaBla = (payload) => {
return axios.post('/some-url', payload)
.then(response => response)
.catch(err => err);
}
And then I'm using this with redux-saga like this:
function* createBlaBlaFlow(action) {
try {
const response = yield call(createBlaBla, action.payload);
if (response) {
yield put({
type: CREATE_BLA_BLA_SUCCESS
});
}
} catch (err) {
// I need the error data here ..
yield put({
type: CREATE_BLA_BLA_FAILURE,
payload: 'failed to create bla-bla'
});
}
}
In case of some error on the backend - like invalid data send to the backend - it returns a 400 response with some data:
{
"code":"ERR-1000",
"message":"Validation failed because ..."
"method":"POST",
"errorDetails":"..."
}
But I don't receive this useful data in the catch statement inside the saga. I can console.log() the data in the axios catch statement, also I can get it inside the try statement in the saga, but it never arrives in the catch.
Probably I need to do something else? ... Or the server shouldn't return 400 response in this case?

So, I came up with two solutions of this problem.
===
First one - very dump workaround, but actually it can be handy in some specific cases.
In the saga, right before we call the function with the axios call inside, we have a variable for the errors and a callback that sets that variable:
let errorResponseData = {};
const errorCallback = (usefulErrorData) => {
errorResponseData = usefulErrorData;
};
Then - in the axios method we have this:
export const createBlaBla = (payload, errCallback) => {
return axios.post('/some-url', payload)
.then(response => response)
.catch(err => {
if (err && err.response.data && typeof errCallback === 'function') {
errCallback(err.response.data);
}
return err;
});
}
This way, when we make request and the backend returns errors - we'll call the callback and will provide the errors from the backend there. This way - in the saga - we have the errors in a variable and can use it as we want.
===
However, another solution came to me from another forum.
The problem I have is because in the method with the axios call I have catch, which means that the errors won't bubble in the generator. So - if we modify the method with the axios call like this:
export const createBlaBla = (payload) => {
return axios.post('/some-url', payload)
}
Then in the catch statement in the saga we'll have the actual backend error.
Hope this helps someone else :)

In your API call you can do the following:
const someAPICall = (action) => {
return axios.put(`some/path/to/api`, data, {
withCredentials: true,
validateStatus: (status) => {
return (status == 200 || status === 403);
}
});
};
Please note the validateStatus() part - this way when axios will encounter 200 or 403 response, it will not throw Error and you will be able to process the response after
const response = yield call(someAPICall, action);
if (response.status === 200) {
// Proceed further
} else if (response.status === 403) {
// Inform user about error
} else {
...
}

Related

How to use refetch method to send different queries using useQuery in react-query

I'm working with react-query in ReactJS to build a website.
I have the following code, where I fetch some data using useQuery():
const { error, data: movie, status, refetch } = useQuery({
queryKey: 'key1',
queryFn: async () => {
return await Promise.all([
axios.get(`API-1.com/get-some-data`), /*getting some data from api-1 */
axios.get(`API-2.com/get-some-data`), /*getting some data from api-2 */
]).then(([api1, ap2]) => {
return { data1: api1, data2: api2 }
})
}
})
The problem I'm facing is getting somtimes a 404 response from one of the apis, so I need to refetch the data just from the other api that doesn't cause the error.
I tried to use the refetch method inside onError, but it can't be used with parameters.
onError: (error) => {
refetch() /* refetch can't be used with parameters */
},
How can I handle this problem?
I ended up doing the following:
Writing the fetching part in an external function (so I can call the function with parameters)
Adding a try-catch block inside the queryFn to handle the errors
/********* this is the function where I fetch the data. *********/.
/********* with a param that tells me if i'll call API-2 or not *********/
const fetchData = async ({ isDataFoundInApi2 }) => {
return await Promise.all([
axios.get(`API-1.com/get-some-data`),
(!isDataFoundInApi2 && axios.get(`API-2.com/get-some-data`)),
]).then(([api1, api2]) => {
return { data1: api1, data2: api2 }
})
/********* the useQuery where the above method is called *********/
/********* first it sends true as a bool value, then if no *********/
/********* data was found in API-2, it sends false *********/
/********* from `catch` to prevent the fetching from API-2 *********/
const { error, data: movie, status } = useQuery({
queryKey: 'key1',
queryFn: async () => {
try {
return await fetchData({})
} catch (error) {
if (error.response?.status === 404)
return await fetchData({ isDataFoundInApi2: true }) // true means call only the first api
}
},
})

how to go from axios interceptor to then

this is my code
axios
.post("/api/addcart", body)
.then((response) => {
if (response.data.success) {
alert("success");
} else {
alert(response.data.message);
console.log(response.data);
}
}) .catch(() => {
console.log("catch");
});
Created a logic that re-requests when JWT expires with axios interceptor
axios.interceptors.response.use(
function (response) {
return response;},
async function (error) {
const originalRequest = error.config;
console.log(error.response.status);
if (error.response.status === 406) {
axios.post("/api/reissue").then((response) => {
console.log(response.data.success);
if (response.data.success) {
originalRequest._retry = true;
return axios(originalRequest);
} //Removed irrelevant code for readability
return;
// return axios(error.config);
}
return Promise.reject(error);
}
);
The problem I'm having here is after the reissue(jwt token renewal request) is successful.
406 is the response code given when the token expires (I set it temporarily)
I want the alert(success) to be generated by going back to the code above after the access token is updated But this request seems to go to catch
Going back to the beginning, I want to renew if the token has expired and go back to then if the renewal request is successful, but I don't know how. Please let me know if there is a better way

Nuxt axios - how to reject a success response to error

I define axios like below
$axios.onResponse((response) => {
if (response.data.status == 500}
return Promise.reject(response)
}
})
$axios.onError((err) => {
console.log(err)
})
and in fetch i call
async fetch () {
await this.$axios.$get('myapi')
}
but i get error like
RangeError
Maximum call stack size exceeded
I try to reject a success response to error but it not working in ssr. How to fix that thank.
Generally, using try-catch statements are preferred over event handlers (where appropriate).
Try something like this:
async fetch() {
try {
const response = await this.$axios.$get('myapi');
if (response.data.status == 500) {
throw new Error("Some error message");
} else {
// Success action here
}
} catch(err) {
console.log(err);
}
}
Is there any particular reason you're returning a rejected Promise when you get a 500 error? Is there any reason not to throw a generalized error message instead?

Axios- redirect in issue

I'm having trouble getting redirects to work after accepting a get request from Axios. I do know that the request is being sent and that it at least gets some response from the URL route,
when i console response.data.redirect it return undefined
const onSubmitHandler = (e) => {
e.preventDefault()
axios.get('/get/user')
.then(function (response) {
if (response.data.redirect == '/' || response.data.redirect == '/login' ) {
window.location = "/login"
} else {
console.log(response.data)
}
})
.catch(function(error) {
window.location = "/login"
})
}
I received status code 302 but it automatically redirects to the page without page refresh shows the redirected page on the same page in a div section.
Anyone knows how can I use interceptor with Axios such that if the status code fall in 200 series then does something else page refresh. I checked the Axios docs but there is no implementation
Try this for interceptors
axios.interceptors.response.use(
response => {
return response.data;
},
err => {
//return new Promise((resolve, reject) => {
//err.response.status for getting error status
// }
throw err;
});
}
);

How to catch a 401 (or other status error) in an angular service call?

Using $http I can catch errors like 401 easily:
$http({method: 'GET', url: 'http://localhost/Blog/posts/index.json'}).
success(function(data, status, headers, config) {
$scope.posts = data;
}).
error(function(data, status, headers, config) {
if(status == 401)
{
alert('not auth.');
}
$scope.posts = {};
});
But how can I do something similar when using services instead. This is how my current service looks:
myModule.factory('Post', function($resource){
return $resource('http://localhost/Blog/posts/index.json', {}, {
index: {method:'GET', params:{}, isArray:true}
});
});
(Yes, I'm just learning angular).
SOLUTION (thanks to Nitish Kumar and all the contributors)
In the Post controller I was calling the service like this:
function PhoneListCtrl($scope, Post) {
$scope.posts = Post.query();
}
//PhoneListCtrl.$inject = ['$scope', 'Post'];
As suggested by the selected answer, now I'm calling it like this and it works:
function PhoneListCtrl($scope, Post) {
Post.query({},
//When it works
function(data){
$scope.posts = data;
},
//When it fails
function(error){
alert(error.status);
});
}
//PhoneListCtrl.$inject = ['$scope', 'Post'];
in controller call Post like .
Post.index({},
function success(data) {
$scope.posts = data;
},
function err(error) {
if(error.status == 401)
{
alert('not auth.');
}
$scope.posts = {};
}
);
Resources return promises just like http. Simply hook into the error resolution:
Post.get(...).then(function(){
//successful things happen here
}, function(){
//errorful things happen here
});
AngularJS Failed Resource GET
$http is a service just like $resource is a service.
myModule.factory('Post', function($resource){
return $http({method: 'GET', url: 'http://localhost/Blog/posts/index.json'});
});
This will return the promise. You can also use a promise inside your factory and chain that so your factory (service) does all of the error handling for you.