Reputation: 3908
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!')
}
)
}
...
}
Upvotes: 349
Views: 925226
Reputation: 1994
If you want to gain access to the whole error body, do this:
async function login(reqBody) {
try {
let res = await Axios({
method: 'post',
url: 'https://myApi.com/path/to/endpoint',
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;
}
}
Upvotes: 144
Reputation: 49301
creating axios instance:
export const api = axios.create({
baseURL: '/api'
});
-this is middleware for response
api.interceptors.response.use(
(res) => res,
(err) => {
if (err.response && err.response.status >= 500) {
// Handling for server errors (status code >= 500)
const { response } = err;
const message = createErrorMessage(err);
if (message) {
// Return the error or additional information for further handling
// you could populate the store here if you have a global store
return {
error: {
contentType: response.headers['Content-Type'] || response.headers['content-type'],
message: createErrorMessage(err)
}
};
}
}
// this will be returned if the error status is less than 500
// this will be caught in the try/catch block when axios fetching executed. so you could catch and handle in there
return Promise.reject(err); // Rejection if error doesn't meet conditions
}
);
-this is utility function used above
export const createErrorMessage = (error: unknown) => {
// When Axios encounters an error during an HTTP request or response handling, it creates a specific Axios error object
if (axios.isAxiosError(error)) {
const apiError = error.response?.data;
if (typeof apiError === 'string' && (apiError as string).length > 0) {
return apiError;
}
return apiError?.message || apiError?.error || error.message;
}
// Network errors
// timeout errors
// CORS errors
if (error instanceof Error) {
return error.message;
}
if (
error &&
typeof error === 'object' &&
'message' in error &&
typeof error.message === 'string'
) {
return error.message;
}
return 'Generic error message';
};
Upvotes: 1
Reputation: 242
https://stackabuse.com/handling-errors-with-axios/
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
}
}
Upvotes: 3
Reputation: 7249
If you want to handle all basic errors in your request module, without needing to use catch
on each call, then the Axios approach is to use an interceptor on the responses:
axios.interceptors.response.use(function (response) {
// Optional: Do something with response data
return response;
}, function (error) {
// Do whatever you want with the response error here:
// But, be SURE to return the rejected promise, so the caller still has
// the option of additional specialized handling at the call-site:
return Promise.reject(error);
});
If you return the error from your axios interceptor, then you can still also use the conventional approach via a catch()
block, like below:
axios.get('/api/xyz/abcd')
.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);
}
});
Upvotes: 466
Reputation: 101
I've been in a scenario where I didn't have access to the back-end of the application and a tool was used to validate the fields, returning something like response.data
:
"Error: Username 'UsuarioTeste' is already taken.."
As it was a standard message, I needed to adapt it to present it to the user, I used the process below:
.catch((error) => {
const errosCadastro = error.response.data;
const listaErros = errosCadastro.split("\n");
listaErros.map(erro => {
if (erro.includes("Error")) {
const start = erro.indexOf(":") + 2;
const end = erro.indexOf("'", start) - 1;
const fieldName = erro.substring(start, end);
const ErroCampo =
fieldName == "Username" ? `Já existe um usuário cadastrado com o nome: <span style="font-weight: bold;"> ${this.name}</span>. <br>`
: fieldName == "Email" ? `Já existe um usuário cadastrado com o email: <span style="font-weight: bold;"> ${this.email}</span>. <br>`
: fieldName == "registro" ? `Já existe um usuário cadastrado com o registro: <span style="font-weight: bold;"> ${this.record}</span>. <br>`
: "";
errorMessage = errorMessage.concat(ErroCampo);
}
})
Upvotes: 0
Reputation:
axios
.get(`${API_BASE_URL}/students`)
.then(res => {
return res.data
})
.then((data)=> {
console.log(data)
})
.catch(error => {
console.log(error)
})
try this way, it's working fine
Upvotes: 4
Reputation: 1961
let response;
await axios({ method, url, data: body, headers })
.then(data => { response = data })
.catch(error => { response = error.response; });
You'll get all type of responses in this response
object, no need to worry about axios errors, you can handle things based on the response.status
Upvotes: 0
Reputation: 51
If I understand you correctly, you want some kind of global handler, so you don't have to attach a catch handler to every request you make. There is a window event for that called unhandledrejection
.
You can read more about this Event in the official documentation: https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event
Here is how you can attach a listener for this Event:
window.addEventListener('unhandledrejection', (event) => {
// Handle errors here...
});
Upvotes: 0
Reputation: 1456
If 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);
}
Upvotes: 39
Reputation: 473
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
}
Upvotes: 8
Reputation: 162
One 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
.....
.....
}
.....
.....
}
Upvotes: 2
Reputation: 1374
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!')
}
)
});
}
Upvotes: 4
Reputation: 141
I 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.
Upvotes: 8
Reputation: 1970
You 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)
})
Upvotes: 104
Reputation: 14793
call 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', 'https://myApi.com/path/to/endpoint')
} catch (error) {
// either handle errors or don't
}
Upvotes: 2