I'm trying to understand javascript promises better with Axios. What I pretend is to handle all errors in Request.js and only call the request function from anywhere without having to use catch().
In this example, the response to the request will be 400 with an error message in JSON.
This is the error I'm getting:
Uncaught (in promise) Error: Request failed with status code 400
The only solution I find is to add .catch(() => {}) in Somewhere.js but I'm trying to avoid having to do that. Is it possible?
Here's the code:
Request.js
export function request(method, uri, body, headers) { let config = { method: method.toLowerCase(), url: uri, baseURL: API_URL, headers: { 'Authorization': 'Bearer ' + getToken() }, validateStatus: function (status) { return status >= 200 && status < 400 } } ... return axios(config).then( function (response) { return response.data } ).catch( function (error) { console.log('Show error notification!') return Promise.reject(error) } ) } Somewhere.js
export default class Somewhere extends React.Component { ... callSomeRequest() { request('DELETE', '/some/request').then( () => { console.log('Request successful!') } ) } ... } 410 Answers
Actually, it's not possible with axios as of now. The status codes which falls in the range of 2xx only, can be caught in .then().
A conventional approach is to catch errors in the catch() block like below:
axios.get('/api/xyz/abcd') .catch(function (error) { if (error.response) { // Request made and server responded 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 console.log(error.request); } else { // Something happened in setting up the request that triggered an Error console.log('Error', error.message); } }); Another approach can be intercepting requests or responses before they are handled by then or catch.
axios.interceptors.request.use(function (config) { // Do something before request is sent return config; }, function (error) { // Do something with request error return Promise.reject(error); }); // Add a response interceptor axios.interceptors.response.use(function (response) { // Do something with response data return response; }, function (error) { // Do something with response error return Promise.reject(error); }); 9If you want to gain access to the whole the error body, do it as shown below:
async function login(reqBody) { try { let res = await Axios({ method: 'post', url: ' data: reqBody }); let data = res.data; return data; } catch (error) { console.log(error.response); // this is the main part. Use the response property from the error object return error.response; } } 1You can go like this: error.response.data
In my case, I got error property from backend. So, I used error.response.data.error
My code:
axios .get(`${API_BASE_URL}/students`) .then(response => { return response.data }) .then(data => { console.log(data) }) .catch(error => { console.log(error.response.data.error) }) 0If you wan't to use async await try
export const post = async ( link,data ) => { const option = { method: 'post', url: `${URL}${link}`, validateStatus: function (status) { return status >= 200 && status < 300; // default }, data }; try { const response = await axios(option); } catch (error) { const { response } = error; const { request, ...errorObject } = response; // take everything but 'request' console.log(errorObject); } 1I tried using the try{}catch{} method but it did not work for me. However, when I switched to using .then(...).catch(...), the AxiosError is caught correctly that I can play around with. When I try the former when putting a breakpoint, it does not allow me to see the AxiosError and instead, says to me that the caught error is undefined, which is also what eventually gets displayed in the UI.
Not sure why this happens I find it very trivial. Either way due to this, I suggest using the conventional .then(...).catch(...) method mentioned above to avoid throwing undefined errors to the user.
If I understand correctly you want then of the request function to be called only if request is successful, and you want to ignore errors. To do that you can create a new promise resolve it when axios request is successful and never reject it in case of failure.
Updated code would look something like this:
export function request(method, uri, body, headers) { let config = { method: method.toLowerCase(), url: uri, baseURL: API_URL, headers: { 'Authorization': 'Bearer ' + getToken() }, validateStatus: function (status) { return status >= 200 && status < 400 } } return new Promise(function(resolve, reject) { axios(config).then( function (response) { resolve(response.data) } ).catch( function (error) { console.log('Show error notification!') } ) }); } 1call the request function from anywhere without having to use catch().
First, while handling most errors in one place is a good Idea, it's not that easy with requests. Some errors (e.g. 400 validation errors like: "username taken" or "invalid email") should be passed on.
So we now use a Promise based function:
const baseRequest = async (method: string, url: string, data: ?{}) => new Promise<{ data: any }>((resolve, reject) => { const requestConfig: any = { method, data, timeout: 10000, url, headers: {}, }; try { const response = await axios(requestConfig); // Request Succeeded! resolve(response); } catch (error) { // Request Failed! if (error.response) { // Request made and server responded reject(response); } else if (error.request) { // The request was made but no response was received reject(response); } else { // Something happened in setting up the request that triggered an Error reject(response); } } }; you can then use the request like
try { response = await baseRequest('GET', ') } catch (error) { // either handle errors or don't } 2One way of handling axios error for response type set to stream that worked for me.
..... ..... try{ ..... ..... // make request with responseType: 'stream' const url = "your url"; const response = axios.get(url, { responseType: "stream" }); // If everything OK, pipe to a file or whatever you intended to do // with the response stream ..... ..... } catch(err){ // Verify it's axios error if(axios.isAxios(err)){ let errorString = ""; const streamError = await new Promise((resolve, reject) => { err.response.data .on("data", (chunk) => { errorString += chunk; } .on("end", () => { resolve(errorString); } }); // your stream error is stored at variable streamError. // If your string is JSON string, then parse it like this const jsonStreamError = JSON.parse(streamError as string); console.log({ jsonStreamError }) // or do what you usually do with your error message ..... ..... } ..... ..... } For reusability:
create a file errorHandler.js:
export const errorHandler = (error) => { const { request, response } = error; if (response) { const { message } = response.data; const status = response.status; return { message, status, }; } else if (request) { //request sent but no response received return { message: "server time out", status: 503, }; } else { // Something happened in setting up the request that triggered an Error return { message: "opps! something went wrong while setting up request" }; } }; Then, whenever you catch error for axios:
Just import error handler from errorHandler.js and use like this. try { //your API calls } catch (error) { const { message: errorMessage } = errorHandlerForAction(error); //grab message } let res = await axios.get('/my-api-route'); // Work with the response... } catch (err) { if (err.response) { // The client was given an error response (5xx, 4xx) } else if (err.request) { // The client never received a response, and the request was never left } else { // Anything else } } try { let res = await axios.get('/my-api-route'); // Work with the response... } catch (err) { if (err.response) { // The client was given an error response (5xx, 4xx) } else if (err.request) { // The client never received a response, and the request was never left console.log(err.request); } else { // Anything else } }