Как обрабатывать ошибки при работе с API?
Обработка ошибок при работе с API в React-приложениях играет важнейшую роль в обеспечении стабильности, предсказуемости и хорошего пользовательского опыта. Ошибки могут возникать по множеству причин — от сетевых сбоев и таймаутов до ошибок на сервере и некорректных данных от пользователя. Грамотная обработка ошибок включает как технический контроль (try/catch, .catch(), status-коды), так и отображение полезной информации пользователю.
Классы ошибок
Ошибки при работе с API можно условно разделить на несколько видов:
-
Сетевые ошибки (Network Errors) — отсутствие подключения, таймаут, недоступность сервера.
-
Ошибки клиента (4xx) — неправильный запрос, неавторизован, доступ запрещён, не найдено.
-
Ошибки сервера (5xx) — внутренняя ошибка сервера, недоступность API.
-
Ошибки данных — неожиданный формат JSON, пустые поля, некорректный ответ.
-
Ошибки приложения — неправильная логика обработки ответа или взаимодействия.
Обработка ошибок с fetch
fetch не вызывает исключение при 404, 500 и других ошибках статуса — только при сетевой ошибке. Поэтому нужно вручную проверять response.ok.
async function fetchData() {
try {
const res = await fetch('https://api.example.com/data');
if (!res.ok) {
throw new Error(\`Ошибка: ${res.status} ${res.statusText}\`);
}
const data = await res.json();
return data;
} catch (error) {
console.error('Ошибка при запросе:', error.message);
throw error; // можно пробросить дальше
}
}
Обработка ошибок с axios
В axios ошибка пробрасывается автоматически при любом неудачном ответе (4xx, 5xx). Сам объект ошибки содержит подробности (error.response, error.request, error.message).
import axios from 'axios';
async function getData() {
try {
const res = await axios.get('https://api.example.com/data');
return res.data;
} catch (error) {
if (error.response) {
// Сервер вернул ошибку
console.error(\`Ошибка ${error.response.status}:\`, error.response.data);
} else if (error.request) {
// Запрос был отправлен, но ответ не получен
console.error('Нет ответа от сервера');
} else {
// Ошибка в настройке запроса
console.error('Ошибка при создании запроса:', error.message);
}
}
}
Отображение ошибки в React-компоненте
function Example() {
const \[data, setData\] = useState(null);
const \[error, setError\] = useState(null);
useEffect(() => {
async function loadData() {
try {
const res = await fetch('https://api.example.com/data');
if (!res.ok) throw new Error(\`Ошибка: ${res.status}\`);
const json = await res.json();
setData(json);
} catch (err) {
setError(err.message);
}
}
loadData();
}, \[\]);
if (error) return <p>Произошла ошибка: {error}</p>;
if (!data) return <p>Загрузка...</p>;
return <div>{JSON.stringify(data)}</div>;
}
Обработка ошибок в React Query
React Query автоматически распознаёт ошибки и предоставляет isError, error, status:
const { data, error, isError, isLoading } = useQuery({
queryKey: \['user'\],
queryFn: () => axios.get('/api/user').then(res => res.data)
});
if (isError) {
return <p>Ошибка: {error.message}</p>;
}
Для mutation:
const mutation = useMutation({
mutationFn: (data) => axios.post('/api/save', data),
onError: (error) => {
console.error('Ошибка при сохранении:', error.message);
},
});
Обработка ошибок в SWR
const { data, error } = useSWR('/api/user', fetcher);
if (error) return <p>Ошибка загрузки данных</p>;
Обработка ошибок на уровне интерфейса
Пользователь должен понимать:
-
Что пошло не так: "Не удалось загрузить данные".
-
Что делать дальше: "Попробуйте позже", "Проверьте подключение", "Перезагрузите страницу".
-
Что не нужно делать: Не показывать технические stack trace'и и JSON-объекты.
Пример:
{error && (
<div className="error">
<p>⚠️ Не удалось загрузить данные. Попробуйте позже.</p>
<button onClick={retry}>Повторить попытку</button>
</div>
)}
Повторные попытки и резервные стратегии
Многие библиотеки поддерживают retry при ошибке:
В React Query:
useQuery({
queryKey: \['user'\],
queryFn: fetchUser,
retry: 3, // повторит 3 раза
retryDelay: attemptIndex => attemptIndex \* 1000, // задержка между попытками
});
В axios (через плагин axios-retry):
npm install axios-retry
import axiosRetry from 'axios-retry';
axiosRetry(axios, { retries: 3, retryDelay: axiosRetry.exponentialDelay });
Интерцепторы для глобальной обработки (axios)
axios.interceptors.response.use(
res => res,
error => {
if (error.response?.status === 401) {
// редирект на логин
}
return Promise.reject(error);
}
);
Выделение кастомного хука useApi
function useApi(url) {
const \[data, setData\] = useState();
const \[error, setError\] = useState();
useEffect(() => {
fetch(url)
.then(res => {
if (!res.ok) throw new Error('Ошибка загрузки');
return res.json();
})
.then(setData)
.catch(setError);
}, \[url\]);
return { data, error };
}
Логгирование и мониторинг
Для продакшн-приложений важно не только отобразить ошибку пользователю, но и зафиксировать её в логах. Используют:
-
Sentry — отслеживание ошибок в реальном времени.
-
LogRocket, Datadog, Bugsnag — поведение пользователя и ошибки.
-
Custom logging — отправка ошибок на собственный сервер.
catch (error) {
logToServer(error); // записать ошибку в базу
}
Типичные ошибки и советы
-
Проверяй response.ok при использовании fetch.
-
Не показывай пользователю техническую информацию.
-
Логируй ошибки для разработчиков, но скрывай детали от пользователей.
-
Показывай кнопку "Повторить попытку" или автоматический retry.
-
Используй хуки, чтобы не дублировать логику обработки в разных компонентах.