Рабочий запрос через Fetch API состоит из четырёх обязательных действий: дождаться fetch, проверить response.ok, прочитать тело подходящим методом и обработать ошибку интерфейсом. fetch отклоняет Promise при части сетевых сбоев, но обычный ответ 404 или 500 сам по себе не попадает в catch. Поэтому проверка статуса должна быть написана явно до response.json().
Fetch API предоставляет Promise-интерфейс для HTTP-запросов в браузере. GET обычно читает ресурс, POST отправляет данные в теле. Но хороший пример должен показывать не только счастливый путь: пользователь должен увидеть загрузку, серверную ошибку, сетевой сбой и корректный результат, а кнопка не должна отправить десять одинаковых запросов.
URL, метод, заголовки и тело выражают контракт API.
Статус проверяется до чтения JSON или текста.
loading, success, empty и error являются отдельными состояниями.
Минимальный GET-запрос с правильной проверкой
Без второго аргумента fetch(url) выполняет GET. Возвращаемый Promise разрешается объектом Response, когда браузер получил статус и заголовки. Метод response.json() тоже асинхронный и читает тело один раз. Между этими шагами проверьте ok, который равен true для статусов диапазона 200.
async function loadTasks() {
const response = await fetch('/api/tasks');
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const tasks = await response.json();
return tasks;
}| Шаг | Что возвращается | Частая ошибка |
|---|---|---|
fetch(url) | Promise<Response> | Забыть await |
response.ok | Булево значение | Считать 404 автоматическим reject |
response.status | HTTP-код | Не сохранить код для сообщения |
response.json() | Promise с разобранным телом | Забыть второй await |
catch | Сетевые и брошенные ошибки | Показать только console.log |

Статус проверяется до чтения тела, а результат проходит в отдельное состояние интерфейса.
response.json() даст SyntaxError и скроет исходный статус. Для общей обёртки проверьте заголовок content-type и читайте текст, если JSON не обещан.POST с JSON: method, headers и body
- Проверьте URL и HTTP-метод в документации API.
- Создайте обычный объект данных без DOM-узлов и функций.
- Укажите
Content-Type: application/json, если сервер ждёт JSON. - Преобразуйте объект через
JSON.stringifyвbody. - Проверьте
response.okдо чтения ответа. - Отобразите результат и верните кнопке доступное состояние.
GET не должен иметь тело. Для POST объект JavaScript нужно сериализовать, иначе в сеть может уйти строка [object Object]. Заголовок Content-Type сообщает серверу формат тела, но сам не преобразует данные. Метод и формат должны соответствовать документации конкретного endpoint.
async function createTask(title) {
const response = await fetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title }),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
GET получает ресурс, а POST добавляет метод, формат и сериализованное тело.
После отправки откройте DevTools и проверьте Request Method, Request Payload, Content-Type, статус и Response. Если тело выглядит иначе, чем ожидалось, исправляйте сериализацию до поиска проблемы на сервере.
Как разделить HTTP, сеть, JSON и отмену
У запроса несколько независимых точек отказа. Сеть может быть недоступна, сервер может вернуть 500, тело может оказаться невалидным JSON, а пользователь может уйти со страницы и отменить операцию. Одно сообщение «что-то пошло не так» удобно для первого прототипа, но бесполезно для диагностики.
| Слой | Пример | Как обнаружить |
|---|---|---|
| Сеть | Нет соединения или DNS | fetch отклоняет Promise |
| HTTP | 404, 401, 500 | !response.ok |
| Формат | Вместо JSON пришёл HTML | Ошибка response.json() и Content-Type |
| CORS | Ответ закрыт политикой | Console и Network |
| Отмена | AbortController | Ошибка с именем AbortError |
async function requestJSON(url, options = {}) {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const type = response.headers.get('content-type') || '';
if (!type.includes('application/json')) {
throw new TypeError('Сервер вернул не JSON');
}
return response.json();
}
Разные слои отказа обнаруживаются разными проверками.
Ошибки Fetch API, которые проходят незаметно
Код без await выводит Promise вместо данных. Код без проверки ok пытается обработать тело ошибки как успешный объект. Бесконечный индикатор появляется, когда состояние loading выключается только в счастливой ветке. Для интерфейса удобно использовать try, catch и finally.
И fetch, и response.json возвращают Promise. Дождаться нужно обоих этапов.
HTTP-ответ получен успешно на сетевом уровне. Проверьте response.ok самостоятельно.
Сериализуйте JSON и укажите соответствующий Content-Type.
Повторные клики создают дубликаты POST. Отключайте действие на время запроса.
Включите loading перед запросом и выключайте в finally. Этот блок выполняется после успеха и после ошибки. Отдельно решите, можно ли повторить запрос автоматически: для POST повтор может создать дубликат без поддержки идемпотентности сервером.
Практика: загрузка и создание задач на странице
Соберите модуль с двумя функциями: loadTasks для GET и createTask для POST. Обработчик формы отвечает только за интерфейс: читает поле, блокирует кнопку, вызывает API-функцию, обновляет список и показывает ошибку. Такое разделение упрощает тестирование.
- URL и метод совпадают с контрактом endpoint.
- POST-тело сериализовано, а Content-Type соответствует формату.
- После fetch явно проверяется response.ok.
- Тело читается ровно один раз подходящим методом.
- Интерфейс показывает loading, empty, success и error.
- Кнопка возвращается в активное состояние через finally.
Какой метод использует fetch по умолчанию?
GET.
Попадает ли ответ 404 в catch автоматически?
Нет. fetch обычно возвращает Response, поэтому нужно проверить response.ok.
Почему у response.json нужен await?
Чтение и разбор тела асинхронны, метод возвращает Promise.
Что передавать в body для JSON?
Строку из JSON.stringify и заголовок Content-Type: application/json.
Как отменить долгий запрос?
Передать signal от AbortController и вызвать abort, когда запрос больше не нужен.
Закрепите асинхронный JavaScript в курсе JavaScript и сравните пример с пошаговым руководством по API. Вывод результатов удобно строить по статье о DOM-манипуляциях.
Если браузер блокирует другой origin, откройте материал от fetch до обработки API в React. В Кодике храните минимальный endpoint, ожидаемый статус и пример JSON как проверяемый контракт.