This may seem stupid, but I’m trying to get the error data when a request fails in Axios.
axios
.get('foo.example')
.then((response) => {})
.catch((error) => {
console.log(error); //Logs a string: Error: Request failed with status code 404
});
Instead of the string, is it possible to get an object with perhaps the status code and content? For example:
Object = {status: 404, reason: 'Not found', body: '404 Not found'}
asked Aug 25, 2016 at 19:13
Sebastian OlsenSebastian Olsen
10.1k9 gold badges42 silver badges89 bronze badges
What you see is the string returned by the toString
method of the error
object. (error
is not a string.)
If a response has been received from the server, the error
object will contain the response
property:
axios.get('/foo')
.catch(function (error) {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
}
});
answered Aug 25, 2016 at 19:34
Nick UraltsevNick Uraltsev
23.7k4 gold badges24 silver badges14 bronze badges
17
With TypeScript, it is easy to find what you want with the right type.
This makes everything easier because you can get all the properties of the type with autocomplete, so you can know the proper structure of your response and error.
import { AxiosResponse, AxiosError } from 'axios'
axios.get('foo.example')
.then((response: AxiosResponse) => {
// Handle response
})
.catch((reason: AxiosError) => {
if (reason.response!.status === 400) {
// Handle 400
} else {
// Handle else
}
console.log(reason.message)
})
Also, you can pass a parameter to both types to tell what are you expecting inside response.data
like so:
import { AxiosResponse, AxiosError } from 'axios'
axios.get('foo.example')
.then((response: AxiosResponse<{user:{name:string}}>) => {
// Handle response
})
.catch((reason: AxiosError<{additionalInfo:string}>) => {
if (reason.response!.status === 400) {
// Handle 400
} else {
// Handle else
}
console.log(reason.message)
})
answered Sep 17, 2019 at 3:03
3
As @Nick said, the results you see when you console.log
a JavaScript Error
object depend on the exact implementation of console.log
, which varies and (imo) makes checking errors incredibly annoying.
If you’d like to see the full Error
object and all the information it carries bypassing the toString()
method, you could just use JSON.stringify:
axios.get('/foo')
.catch(function (error) {
console.log(JSON.stringify(error))
});
answered Feb 14, 2017 at 9:17
daniidanii
5,5132 gold badges21 silver badges23 bronze badges
2
There is a new option called validateStatus
in request config. You can use it to specify to not throw exceptions if status < 100 or status > 300 (default behavior). Example:
const {status} = axios.get('foo.example', {validateStatus: () => true})
answered Mar 31, 2020 at 12:44
You can use the spread operator (...
) to force it into a new object like this:
axios.get('foo.example')
.then((response) => {})
.catch((error) => {
console.log({...error})
})
Be aware: this will not be an instance of Error.
answered Jan 22, 2020 at 20:31
Moses SchwartzMoses Schwartz
2,7181 gold badge19 silver badges31 bronze badges
0
I am using this interceptors to get the error response.
const HttpClient = axios.create({
baseURL: env.baseUrl,
});
HttpClient.interceptors.response.use((response) => {
return response;
}, (error) => {
return Promise.resolve({ error });
});
answered Dec 5, 2016 at 16:41
TanTan
3323 silver badges11 bronze badges
2
In order to get the http status code returned from the server, you can add validateStatus: status => true
to axios options:
axios({
method: 'POST',
url: 'http://localhost:3001/users/login',
data: { username, password },
validateStatus: () => true
}).then(res => {
console.log(res.status);
});
This way, every http response resolves the promise returned from axios.
https://github.com/axios/axios#handling-errors
answered May 7, 2020 at 11:48
Emre TapcıEmre Tapcı
1,68314 silver badges15 bronze badges
Whole error can only be shown using error.response like that :
axios.get('url').catch((error) => {
if (error.response) {
console.log(error.response);
}
});
answered Jul 1, 2021 at 10:07
const handleSubmit = (e) => {
e.preventDefault();
// console.log(name);
setLoading(true);
createCategory({ name }, user.token)
.then((res) => {
// console.log("res",res);
setLoading(false);
setName("");
toast.success(`"${res.data.name}" is created`);
loadCategories();
})
.catch((err) => {
console.log(err);
setLoading(false);
if (err.response.status === 400) toast.error(err.response.data);//explained in GD
});
};
See the console log then you will understand clearly
answered Oct 7, 2022 at 4:08
With Axios
post('/stores', body).then((res) => {
notifyInfo("Store Created Successfully")
GetStore()
}).catch(function (error) {
if (error.status === 409) {
notifyError("Duplicate Location ID, Please Add another one")
} else {
notifyError(error.data.detail)
}
})
answered Sep 3, 2021 at 5:43
It’s indeed pretty weird that fetching only error does not return an object. While returning error.response gives you access to most feedback stuff you need.
I ended up using this:
axios.get(...).catch( error => { return Promise.reject(error.response.data.error); });
Which gives strictly the stuff I need: status code (404) and the text-message of the error.
answered Apr 27, 2021 at 12:14
You can put the error into an object and log the object, like this:
axios.get('foo.example')
.then((response) => {})
.catch((error) => {
console.log({error}) // this will log an empty object with an error property
});
answered Jan 22, 2020 at 21:29
MendyMendy
7,2205 gold badges28 silver badges40 bronze badges
Axios. get('foo.example')
.then((response) => {})
.catch((error) => {
if(error. response){
console.log(error. response. data)
console.log(error. response. status);
}
})
answered Jan 14, 2021 at 5:59
VigneshVignesh
1371 silver badge5 bronze badges
3
It’s my code: Work for me
var jsonData = request.body;
var jsonParsed = JSON.parse(JSON.stringify(jsonData));
// message_body = {
// "phone": "5511995001920",
// "body": "WhatsApp API on chat-api.com works good"
// }
axios.post(whatsapp_url, jsonParsed,validateStatus = true)
.then((res) => {
// console.log(`statusCode: ${res.statusCode}`)
console.log(res.data)
console.log(res.status);
// var jsonData = res.body;
// var jsonParsed = JSON.parse(JSON.stringify(jsonData));
response.json("ok")
})
.catch((error) => {
console.error(error)
response.json("error")
})
answered Jul 4, 2020 at 12:17
I try use axios.interceptors.response and get error.config but i not found any status code or specific error message.
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
console.log(error.response);
return Promise.reject(error);
});
hope that helps
toriiico, CristianeMayara, and mw999 reacted with hooray emoji
I tried to search SO and closed issues but couldn’t find a proper solution.
I wanted to return response.data
in first param, and error.status
or error.response
in second param, but it didn’t work.
The error object doesn’t have any response or status. Seems it consumes output from response.data
returned in the success/first param
axios.interceptors.response.use(res => response.data, function (error) {
// Do something with response error
console.log(error.response);
return Promise.reject(error);
});
This problem is still there, why is closed?
Found this issue via top result on Google. Anyway, the fix for me was ensuring that I call .use
on the instance of Axios that I’m using within my app.
import Axios from 'axios';
export const http = Axios.create({
... config ...
});
And then after:
http.interceptors.response.use(
function(response) {
// response data
return response;
},
function(error) {
debugger; // <-- this should now trigger correctly
// response error
return Promise.reject(error.response); // transform response.response -> response
}
);
Note how I’m referencing http
and not the imported Axios
variable. (replace with your desired export name, etc)
axios
locked and limited conversation to collaborators
May 22, 2020
If you have been searching the web for some information about AXIOS error messages, and would like to understand how to use them, then you have come to the right place.
TLTR; Find the code snippets in the following section
If you search the web on this topic, all you can find is:
- catching error body using axios post
- Unable to catch the body of error
- JS Axios – how to get response body in event of error?
- How to see axios error response JSON in React
The list could have gone on and on, but they all ask for the sample simple question:
How can someone get the actual error information coming from a bad request made with AXIOS.
In this post we are going to provide information on “why” so many people ask for information, and “how” this information can be found.
Why so many people ask for this
If you have ever worked with an api, you perfectly know that request not always go to plan. You hope to always get a lovely response with a status of 200, but this does not always happens.
In many instances the status of our request may return some kind of error (400, 500), and when this happens we would like to be able to have a readable error message.
axios.get('EndpointWithAuthorizedError')
.then((response) => {})
.catch((error) => {
console.log(error);
})
Enter fullscreen mode
Exit fullscreen mode
Unfortunately if the above would ever fail with a meaningful error, we would still see this within our console, as the variable passed by the catch statement seem to be of type “string”.
Error: Request failed with status code 401
Enter fullscreen mode
Exit fullscreen mode
This is actually the main reason why so many people are “forced” to ask for help.
How can we process AXIOS error message
There is actually no magic when processing the error messages. In fact, the catch is in the fact that the variable received by the catch
statement seem to be a string, but in reality it is not.
The AXIOS error message can actually return 3 different structure, depending from what kind of failure it has (crazy right… I thought that too).
Error in setting up the request
This error can happen if we have made an actual mistake in setting up the AXIOS request. It could be something with the data passed, or a configuration setting.
When this happen we can actually find the information we need by accessing the message
parameter of the catch.
axios.get('wrongSetup')
.then((response) => {})
.catch((error) => {
console.log(error.message);
})
//or using destructuring
axios.get('wrongSetup')
.then((response) => {})
.catch(({message) => {
console.log(message);
})
Enter fullscreen mode
Exit fullscreen mode
No response – Network Error
This scenario will take place when our request had no response at all. This can happen when the URL is incorrect, or if the receiving server is down.
When this happen we can access more information about our request bu accessing the request
parameter. This will return the actual “request” information.
axios.get('network error')
.then((response) => {})
.catch((error) => {
console.log(error. request );
})
//or using destructuring
axios.get('network error')
.then((response) => {})
.catch(({ request ) => {
console.log( request );
})
Enter fullscreen mode
Exit fullscreen mode
Request returned with an error status
This is one of the most common, or more specifically the one type of “error” that need to be manage to make sure our web applications function properly.
There are hundreds of status code differently than 200 (https://developer.mozilla.org/en-US/docs/Web/HTTP/Status), that would fit in this category. I am going to list below the most important:
- 400: Bad request
- 401: Unauthorized
- 403: Forbidden
- 404: Not Found
- 500: Internal Server error
- 502: Bad Gateway
When any of the above happen, we would like to know more about the request. In this case there are more information provided to us: data
, status
, header
.
axios.get('errorStatus')
.then((response) => {})
.catch((error) => {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
})
//or using destructuring
axios.get('errorStatus')
.then((response) => {})
.catch(({ response }) => {
console.log(response.data);
console.log(response.status);
console.log(response.headers);
})
Enter fullscreen mode
Exit fullscreen mode
With the above code, we will be able to get all the information we need about the response to our request. These includes the status code, the header and last but not least the data that the server has sent with the request.
When using any kind of API, the data
parameter is going to include essential information, usually used for development purposes, but at times also good to show to the end users.
I hope to have saved you some time, and please feel free to comment, or provide suggestion to improve this post and help future readers
Когда я только начинал работать с Express и пытался разобраться с тем, как обрабатывать ошибки, мне пришлось нелегко. Возникало такое ощущение, будто никто не писал о том, что мне было нужно. В итоге мне пришлось самому искать ответы на мои вопросы. Сегодня я хочу рассказать всё, что знаю об обработке ошибок в Express-приложениях. Начнём с синхронных ошибок.
Обработка синхронных ошибок
Если вам нужно обработать синхронную ошибку, то вы можете, для начала, с помощью инструкции throw
, выдать такую ошибку в обработчике запроса Express. Обратите внимание на то, что обработчики запросов ещё называют «контроллерами», но я предпочитаю использовать термин «обработчик запросов» так как он кажется мне понятнее.
Вот как это выглядит:
app.post('/testing', (req, res) => {
throw new Error('Something broke! ')
})
Такие ошибки можно перехватить с помощью обработчика ошибок Express. Если вы не написали собственный обработчик ошибок (подробнее об этом мы поговорим ниже), то Express обработает ошибку с помощью обработчика, используемого по умолчанию.
Вот что делает стандартный обработчик ошибок Express:
- Устанавливает код состояния HTTP-ответа в значение 500.
- Отправляет сущности, выполнившей запрос, текстовый ответ.
- Логирует текстовый ответ в консоль.
Сообщение об ошибке, выведенное в консоль
Обработка асинхронных ошибок
Для обработки асинхронных ошибок нужно отправить ошибку обработчику ошибок Express через аргумент next
:
app.post('/testing', async (req, res, next) => {
return next(new Error('Something broke again! '))
})
Вот что попадёт в консоль при логировании этой ошибки.
Сообщение об ошибке, выведенное в консоль
Если вы пользуетесь в Express-приложении конструкцией async/await, то вам понадобится использовать функцию-обёртку, наподобие express-async-handler. Это позволяет писать асинхронный код без блоков try/catch. Подробнее об async/await в Express можно почитать здесь.
const asyncHandler = require('express-async-handler')
app.post('/testing', asyncHandler(async (req, res, next) => {
// Сделать что-нибудь
}))
После того, как обработчик запроса обёрнут в express-async-handler
, то можно, так же, как было описано выше, выбросить ошибку с использованием инструкции throw
. Эта ошибка попадёт к обработчику ошибок Express.
app.post('/testing', asyncHandler(async (req, res, next) => {
throw new Error('Something broke yet again! ')
}))
Сообщение об ошибке, выведенное в консоль
Написание собственного обработчика ошибок
Обработчики ошибок Express принимают 4 аргумента:
- error
- req
- res
- next
Размещать их нужно после промежуточных обработчиков и маршрутов.
app.use(/*...*/)
app.get(/*...*/)
app.post(/*...*/)
app.put(/*...*/)
app.delete(/*...*/)
// Собственный обработчик ошибок нужно поместить после всех остальных промежуточных обработчиков
app.use((error, req, res, next) => { /* ... */ })
Если создать собственный обработчик ошибок, то Express прекратит использование стандартного обработчика. Для того чтобы обработать ошибку, нужно сформировать ответ для фронтенд-приложения, которое обратилось к конечной точке, в которой возникла ошибка. Это означает, что нужно выполнить следующие действия:
- Сформировать и отправить подходящий код состояния ответа.
- Сформировать и отправить подходящий ответ.
То, какой именно код состояния подойдёт в каждом конкретном случае, зависит от того, что именно произошло. Вот список типичных ошибок, к обработке которых вы должны быть готовы:
- Ошибка
400 Bad Request
. Используется в двух ситуациях. Во-первых — тогда, когда пользователь не включил в запрос необходимое поле (например — в отправленной платёжной форме не заполнено поле со сведениями о кредитной карте). Во-вторых — тогда, когда в запросе содержатся некорректные данные (например — ввод в поле пароля и в поле подтверждения пароля разных паролей). - Ошибка
401 Unauthorized
. Этот код состояния ответа применяется в том случае, если пользователь ввёл неправильные учётные данные (вроде имени пользователя, адреса электронной почты или пароля). - Ошибка
403 Forbidden
. Используется в тех случаях, когда пользователю не разрешён доступ к конечной точке. - Ошибка
404 Not Found
. Применяется в тех случаях, когда конечную точку невозможно обнаружить. - Ошибка
500 Internal Server Error
. Применяется тогда, когда запрос, отправленный фронтендом, сформирован правильно, но на бэкенде при этом возникла какая-то ошибка.
После того, как определён подходящий код состояния ответа, его нужно установить с помощью res.status
:
app.use((error, req, res, next) => {
// Ошибка, выдаваемая в ответ на неправильно сформированный запрос
res.status(400)
res.json(/* ... */)
})
Код состояния ответа должен соответствовать сообщению об ошибке. Для этого нужно отправлять код состояния вместе с ошибкой.
Легче всего это сделать с помощью пакета http-errors. Он позволяет отправлять в ошибке три фрагмента информации:
- Код состояния ответа.
- Сообщение, сопутствующее ошибке.
- Любые данные, которые нужно отправить (это необязательно).
Вот как установить пакет http-errors
:
npm install http-errors --save
Вот как этим пакетом пользоваться:
const createError = require('http-errors')
// Создание ошибки
throw createError(status, message, properties)
Рассмотрим пример, который позволит как следует в этом всём разобраться.
Представим, что мы пытаемся обнаружить пользователя по адресу его электронной почты. Но этого пользователя найти не удаётся. В результате мы решаем отправить в ответ на соответствующий запрос ошибку User not found
, сообщающую вызывающей стороне о том, что пользователь не найден.
Вот что нам нужно будет сделать при создании ошибки:
- Установить код состояния ответа как
400 Bad Request
(ведь пользователь ввёл неправильные данные). Это будет наш первый параметр. - Отправить вызывающей стороне сообщение наподобие
User not found
. Это будет второй параметр.
app.put('/testing', asyncHandler(async (req, res) => {
const { email } = req.body
const user = await User.findOne({ email })
// Если пользователь не найден - выбросим ошибку
if (!user) throw createError(400, `User '${email}' not found`)
}))
Получить код состояния можно с помощью конструкции error.status
, а сообщение ошибки — с помощью error.message
:
// Логирование ошибки
app.use((error, req, res, next) => {
console.log('Error status: ', error.status)
console.log('Message: ', error.message)
})
Результат логирования ошибки в консоли
Затем состояние ответа устанавливают с помощью res.status
, а сообщение записывают в res.json
:
app.use((error, req, res, next) => {
// Установка кода состояния ответа
res.status(error.status)
// Отправка ответа
res.json({ message: error.message })
})
Лично я предпочитаю отправлять в подобных ответах код состояния, сообщение и результат трассировки стека. Это облегчает отладку.
app.use((error, req, res, next) => {
// Установка кода состояния ответа
res.status(error.status)
// Отправка ответа
res.json({
status: error.status,
message: error.message,
stack: error.stack
})
})
▍Код состояния ответа, используемый по умолчанию
Если источником ошибки не является createError
, то у неё не будет свойства status
. Вот пример, в котором сделана попытка прочесть несуществующий файл с помощью fs.readFile
:
const fs = require('fs')
const util = require('util')
// Преобразуем readFile из функции, использующей коллбэки, в async/await-функцию.
// Подробности об этом смотрите здесь: https://zellwk.com/blog/callbacks-to-promises
const readFilePromise = util.promisify(fs.readFile)
app.get('/testing', asyncHandler(async (req, res, next) => {
const data = await readFilePromise('some-file')
})
У такого объекта ошибки не будет свойства status
:
app.use((error, req, res, next) => {
console.log('Error status: ', error.status)
console.log('Message: ', error.message)
})
Результат логирования ошибки в консоли
В подобных случаях можно задать код ошибки, используемый по умолчанию. А именно, речь идёт об ошибке 500 Internal Server Error
:
app.use((error, req, res, next) => {
res.status(error.status || 500)
res.json({
status: error.status,
message: error.message,
stack: error.stack
})
})
▍Изменение кода состояния ошибки
Предположим, мы собираемся прочитать некий файл, воспользовавшись данными, предоставленными пользователем. Если такого файла не существует, это значит, что нам нужно выдать ошибку 400 Bad Request
. Ведь в том, что файл найти не удаётся, нет вины сервера.
В подобном случае нужно воспользоваться конструкцией try/catch для перехвата исходной ошибки. Затем нужно воссоздать объект ошибки с помощью createError
:
app.get('/testing', asyncHandler(async (req, res, next) => {
try {
const { file } = req.body
const contents = await readFilePromise(path.join(__dirname, file))
} catch (error) {
throw createError(400, `File ${file} does not exist`)
}
})
▍Обработка ошибок 404
Если запрос прошёл через все промежуточные обработчики и маршруты, но так и не был обработан, это означает, что конечная точка, соответствующая такому запросу, не была найдена.
Для обработки ошибок 404 Not Found
нужно добавить, между маршрутами и обработчиком ошибок, дополнительный обработчик. Вот как выглядит создание объекта ошибки 404:
// Промежуточные обработчики...
// Маршруты...
app.use((req, res, next) => {
next(createError(404))
})
// Обработчик ошибок...
Сведения об ошибке
▍Замечания об ошибке ERR_HTTP_HEADERS_SENT
Не впадайте в панику если видите сообщение об ошибке ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client
. Она возникает из-за того, что в одном и том же обработчике многократно вызывается метод, устанавливающий заголовки ответа. Вот методы, вызов которых приводит к автоматической установке заголовков ответа:
- res.send
- res.json
- res.render
- res.sendFile
- res.sendStatus
- res.end
- res.redirect
Так, например, если вы вызовете методы res.render
и res.json
в одном и том же обработчике ответа, то вы получите ошибку ERR_HTTP_HEADERS_SENT
:
app.get('/testing', (req, res) => {
res.render('new-page')
res.json({ message: '¯_(ツ)_/¯' })
})
В результате, в том случае, если вы сталкиваетесь с этой ошибкой, тщательно проверьте код обработчиков ответа и убедитесь в том, что в нём нет ситуаций, в которых вызывается несколько вышеописанных методов.
▍Обработка ошибок и потоковая передача данных
Если что-то идёт не так при потоковой передаче ответа фронтенду, то можно столкнуться с той же самой ошибкой ERR_HTTP_HEADERS_SENT
.
В подобном случае обработку ошибок нужно передать стандартным обработчикам. Такой обработчик отправит ошибку и автоматически закроет соединение.
app.use((error, req, res, next) => {
// Сделать это нужно только в том случае, если ответ передаётся в потоковом режиме
if (res.headersSent) {
return next(error)
}
// Остальной код обработки ошибок
})
Итоги
Сегодня я рассказал вам всё, что знаю об обработке ошибок в Express. Надеюсь, это поможет вам писать более надёжные Express-приложения.
Уважаемые читатели! Как вы обрабатываете ошибки в своих Node.js-проектах?
Note: If you want to see how to handle these in React, take a look at my new post on that here — handling async errors with axios in react.
Whenever you’re making a backend API call with axios, you have to consider what to do with the .catch()
block of your promise. Now, you might think that your API is highly available and it’ll be running 24/7. You might think that the user workflow is pretty clear, your javascript is sane, and you have unit tests. So when you stare at the catch block when making requests using axios
you might think — «Well… I’ll just console.log it. That’ll be fine.»
axios.get('/my-highly-available-api').then(response => {
// do stuff
})
.catch(err => {
// what now?
console.log(err);
})
But there are so many many many more things that are outside of your control that could throw errors when making API requests. And you probably don’t even know that they’re happening!
This post deals mainly with errors you see in the browser. Things can get pretty funny looking on the back end too — just take a look at 3 things you might see in your backend logs
Below are 3 types of errors that could appear, and how to handle it, when using axios.
Catching axios errors
Below is a snippet I’ve started including in a few JS projects.
axios.post(url, data).then(res => {
// do good things
})
.catch(err => {
if (err.response) {
// client received an error response (5xx, 4xx)
} else if (err.request) {
// client never received a response, or request never left
} else {
// anything else
}
})
Each condition is meant to capture a different type of error.
Checking error.response
If your error object contains a response
field, that means your server responded with a 4xx/5xx error. Usually this is the error we’re most familiar with, and is most straightforward to handle.
Do things like a show a 404 Not Found page/error message if your API returns a 404. Show a different error message if your backend is returning a 5xx or not returning anything at all. You might think your well-architected backend won’t throw errors — but its a matter of when, not if.
Checking error.request
The second class of errors is where you don’t have a response but there’s a request field attached to the error. When does this happen? This happens when the browser was able to make a request, but for some reason, it didn’t see a response.
This can happen if:
- you’re in a spotty network (think underground subway, or a building wireless network)
- if your backend is hanging on each request and not returning a response on time
- if you are making cross-domain requests and you’re not authorized to make the request
- if you’re making cross-domain requests and you are authorized, but the backend API returns an error
One of the more common versions of this error had a message «Network Error» which is a useless error message. We have a front-end and a backend api hosted on different domains, so every backend API call is a cross-domain request.
Due to security constraints on JS in the browser, if you make an API request, and it fails due to crappy networks, the only error you’ll see is «Network Error» which is incredibly unhelpful. It can mean anything from «your device doesn’t have internet connectivity» to «your OPTIONS returned a 5xx» (if you make CORS requests). The reason for «Network Error» is described well here on this StackOverflow answer
All the other types of errors
If your error object doesn’t have the response
or request
field on it, that means its not an axios error and theres likely something else wrong in your app. The error message + stack trace should help you figure out where its coming from.
How do you fix it?
Degrading the user experience
This all depends on your app. For the projects I work on, for each of the features that use those endpoints, we degrade the user experience.
For example, if the request fails, and the page is useless without that data, then we have a bigger error page that will appear and offer users a way out — which sometimes is only a «Refresh the page» button.
Another example, if a request fails for a profile picture in a social media stream, we can show a placeholder image and disable profile picture changes, along with a toaster message explaining why the «update profile picture» button is disabled. However, showing an alert saying «422 Unprocessable Entity» is useless to see as a user.
Spotty networks
The web client I work on is used in school networks which can be absolutely terrible. The availability of your backend barely has anything to do with it, the request sometimes fail to leave the school network.
To solve these types of intermittent network problems, we added in axios-retry. This solved a good amount of the errors we were seeing in production. We added this to our axios setup:
const _axios = require('axios')
const axiosRetry = require('axios-retry')
const axios = _axios.create()
// https://github.com/softonic/axios-retry/issues/87
const retryDelay = (retryNumber = 0) => {
const seconds = Math.pow(2, retryNumber) * 1000;
const randomMs = 1000 * Math.random();
return seconds + randomMs;
};
axiosRetry(axios, {
retries: 2,
retryDelay,
// retry on Network Error & 5xx responses
retryCondition: axiosRetry.isRetryableError,
});
module.exports = axios;
We were able to see that 10% of our users (which are in crappy school networks) were seeing sporadic «Network Errors» and that dropped down to <2% after adding in automatic retries on failure.
^ This is a screenshot of our errors is the count of «Network Errors» as they appear in NewRelic and are showing <1% of requests are erroring out.
Which leads to my last point:
Add error reporting to your front-end
Its helpful to have front-end error/event reporting so that you know whats happening in prod before your users tell you. At my day job, we use NewRelic Browser (paid service — no affiliation, just a fan) to send error events from the front-end. So whenever we catch an exception, we log the error message, along with the stack trace (although thats sometimes useless with minified bundles), and some metadata about the current session so we can try to recreate it.
Other tools that fill this space are Sentry + browser SDK, Rollbar, and a whole bunch of other useful ones listed here
Wrapping up
If you get nothing else out of this, do one thing.
Go to your code base now, and review how you’re handling errors with axios.
- Check if you’re doing automatic retries, and consider adding
axios-retry
if you aren’t - Check that you’re catching errors, and letting the user know that something has happened.
axios.get(...).catch(console.log)
isn’t good enough.
So. How do you handle your errors? Let me know in the comments.
Introduction
Axios is a JavaScript library that uses the Promise API to create HTTP requests with http
in Node.js runtime or XMLHttpRequests
in the browser. Because these requests are promises, they work with the newer async/await syntax, as well as .then()
functions for promise chaining and the .catch()
mechanism for error handling.
try {
let res = await axios.get('/my-api-route');
// Work with the response...
} catch (err) {
// Handle error
console.log(err);
}
In this article, we will see how to handle errors with Axios, as this is very important when making any HTTP calls knowing fully well that there are times when the service you’re calling might not be available or return other unexpected errors. We’ll show the
.then()
/.catch()
method, but primarily use the async/await syntax.
Then and Catch
Promises can be handled in two ways using modern JS — the async/await syntax, which was shown above, as well as .then()
and .catch()
methods. Note that both of these methods can produce the same functionality, but async/await is typically regarded as being easier to work with and requires less boilerplate code in longer promise chains.
Here is how you’d achieve the same thing, but using the then/catch method:
axios.get('/my-api-route')
.then(res => {
// Work with the response...
}).catch(err => {
// Handle error
console.log(err);
});
Both the res
and err
objects are the same as with the async/await syntax.
Handling Errors
In this section, we will look at two primary categories of problems, as well as other issues that we may encounter and how to manage them using Axios. It is critical that you understand that this applies to all types of HTTP queries handled by Axios, including GET
, POST
, PATCH
, and so on.
Here you can see the syntax for the three aspects — this will capture the error; it is crucial to note that this error carries a large error object with a lot of information:
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
} else {
// Anything else
}
}
The differences in the error object, highlighted above in the catch
code, indicate where the request encountered the issue. We’ll look deeper into this in the following sections.
error.response
This is the type of mistake we are most familiar with, and it is much easier to deal with. Many sites display a 404 Not Found page/error message or various response codes based on what the API provides; this is often handled via the response.
If your error object has a response property, it signifies your server returned a 4xx/5xx error. This will assist you choose what sort of message to return to users; the message you’ll want to provide for 4xx may differ from that for 5xx, and if your backend isn’t returning anything at all.
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)
console.log(err.response.data);
console.log(err.response.status);
console.log(err.response.headers);
} else if (err.request) {
// The client never received a response, and the request was never left
} else {
// Anything else
}
}
error.request
This error is most commonly caused by a bad/spotty network, a hanging backend that does not respond instantly to each request, unauthorized or cross-domain requests, and lastly if the backend API returns an error.
Note: This occurs when the browser was able to initiate a request but did not receive a valid answer for any reason.
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
}
}
Earlier we mentioned that the underlying request Axios uses depends on the environment in which it’s being run. This is also the case for the err.request
object. Here the err.request
object is an instance of XMLHttpRequest
when being executed in the browser, whereas it’s an instance of http.ClientRequest
when being used in Node.js.
Other Errors
It’s possible that the error object does not have either a response
or request
object attached to it. In this case it is implied that there was an issue in setting up the request, which eventually triggered an error.
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
} else {
// Anything else
console.log('Error', err.message);
}
}
For example, this could be the case if you omit the URL parameter from the .get()
call, and thus no request was ever made.
Conclusion
In this short article, we looked at how we may handle various sorts of failures and errors in Axios. This is also important for giving the correct message to your application/website visitors, rather than always returning a generic error message, sending a 404, or indicating network problems.