Fetch API

Fetch API #

Fetch — современный способ отправить HTTP-запрос из браузера и получить Response. Метод возвращает Promise, поэтому его удобно сочетать с async/await.


Простой GET-запрос #

const response = await fetch('/api/tracks?limit=5')
const tracks = await response.json()
console.log(tracks.length)

По умолчанию fetch делает GET без тела. URL может быть относительным (/api/...) — браузер подставит origin текущей страницы.

Если сервер отвечает не JSON, а текстом или файлом:

const response = await fetch('/api/export.csv')
const csv = await response.text()

Разбор JSON #

response.json() тоже возвращает Promise: тело читается асинхронно.

async function loadPlaylist(id) {
  const response = await fetch(`/api/playlists/${id}`)
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`)
  }
  return response.json()
}

const playlist = await loadPlaylist('focus-tracks')
console.log(playlist.title)

Важно: fetch не бросает ошибку при статусе 404 или 500 — Promise отклоняется только при сбое сети. Код ответа нужно проверять вручную через response.ok или response.status.


response.ok и response.status #

СвойствоЗначение
statusЧисловой код: 200, 404, 500
oktrue, если статус в диапазоне 200–299
statusTextТекстовая фраза, например «Not Found»
const response = await fetch('/api/orders/999')

console.log(response.status) // 404
console.log(response.ok) // false

if (!response.ok) {
  const message = await response.text()
  console.error('Ошибка:', message)
}

Паттерн «проверить ok, затем парсить тело» защищает от попытки вызвать .json() на HTML-странице ошибки.


Заголовки #

Заголовки передают метаданные: тип контента, авторизацию, язык.

const response = await fetch('/api/cart', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Accept: 'application/json',
  },
  body: JSON.stringify({ sku: 'kb-01', qty: 1 }),
})

Request headers — то, что клиент отправляет. Response headers читаются из объекта response.headers:

const type = response.headers.get('content-type')
if (type?.includes('application/json')) {
  const data = await response.json()
}

Некоторые заголовки браузер выставляет сам (например, User-Agent) или блокирует из JS по правилам безопасности — подробнее в статье про CORS.


POST и другие методы #

await fetch('/api/feedback', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ rating: 5, comment: 'Понятные примеры' }),
})

Для форм и загрузки файлов используют FormData вместо JSON — тело запроса браузер сформирует с нужным Content-Type.


Частые ошибки #

  1. Не проверили response.ok — парсите JSON от ошибки сервера и получаете SyntaxError.
  2. Дважды читаете тело — поток можно прочитать только один раз; сохраните результат в переменную.
  3. Забыли await — в переменной лежит Promise, а не данные.
  4. CORS — запрос ушёл, но браузер скрыл ответ; см. CORS и ошибки запросов.
// плохо: тело прочитано дважды
const response = await fetch('/api/user')
const user = await response.json()
const copy = await response.json() // TypeError

// хорошо
const response = await fetch('/api/user')
const user = await response.json()
processUser(user)

Итог #

  • fetch(url) возвращает Promise с объектом Response.
  • Для JSON: await response.json() после проверки response.ok.
  • Статусы 4xx/5xx не отклоняют Promise fetch — проверяйте ok или status.
  • Заголовки задаются в объекте headers; ответные — через response.headers.get().
  • POST и другие методы настраиваются полем method и body.

См. также #

Часто спрашивают

Почему fetch «падает» на чужом домене?
Браузер блокирует чтение ответа без CORS-заголовков с сервера. На фронте это выглядит как сетевая ошибка TypeError. Подробнее
Когда нужен WebSocket, а когда SSE?
WebSocket — двусторонний канал; SSE — односторонний поток событий с сервера проще для подписок «сервер → клиент». Подробнее