fetch, JSON и AbortController
Правильные запросы, проверка res.ok, query-параметры и отмена.
Обновлено
Что такое fetch
fetch — нативная функция (в браузере, Node и Next) для HTTP-запросов: запросить данные у сервера или отправить их на сервер. Возвращает Promise с объектом Response.
const res = await fetch('/api/posts')
const posts = await res.json()Два await: первый ждёт заголовки ответа, второй читает и разбирает тело.
Ловушка №1: fetch не бросает ошибку на 404 / 500
fetch бросает ошибку только при сетевых проблемах (офлайн, DNS, CORS). Ответ 404 или 500 для него — нормальный ответ.
const res = await fetch('/api/posts')
if (!res.ok) throw new Error(`HTTP ${res.status}`) // res.ok = статус 200–299
const posts = await res.json()Объект Response
| Свойство / метод | Что даёт |
|---|---|
res.ok |
true для 2xx |
res.status |
200, 404… |
res.headers.get('content-type') |
заголовок |
res.json() |
тело, разобранное как JSON (Promise) |
res.text() |
тело как текст |
res.blob() |
файл / изображение |
Тело можно прочитать только один раз.
Виды запросов
| Нужно... | Как |
|---|---|
| прочитать | fetch(url) — по умолчанию GET |
| отправить JSON | method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) |
| отправить форму / файл | method: 'POST', body: new FormData(form) — без Content-Type, его поставит браузер |
| изменить / удалить | method: 'PATCH' / 'DELETE' |
| отправить cookie на другой домен | credentials: 'include' |
await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Привет' }),
})Что такое JSON
JSON (JavaScript Object Notation) — текстовый формат, в котором данные ходят между браузером и сервером. Похож на объект JS, но это строка со строгими правилами: ключи в двойных кавычках, без функций, без undefined, без запятой в конце.
JSON.stringify({ a: 1, d: new Date() }) // '{"a":1,"d":"2026-10-08T..."}' — Date становится строкой
JSON.parse('{"a":1}') // { a: 1 } — бросает SyntaxError на невалидном JSONURL с параметрами
const url = new URL('/api/search', location.origin)
url.searchParams.set('q', 'next js') // автоматическое кодирование: q=next+js
url.searchParams.set('page', '2')
fetch(url)Не склеивайте вручную — символы вроде &, ?, пробела ломают URL.
Отмена и таймаут через AbortController
Старый запрос, ответивший после нового (поиск при вводе), может перезаписать правильный результат — это состояние гонки. Решение: отменить старый запрос.
const controller = new AbortController()
fetch(url, { signal: controller.signal })
controller.abort() // запрос бросает AbortError
fetch(url, { signal: AbortSignal.timeout(5000) }) // автоматическая отмена через 5 сХелпер, написанный один раз
async function api(path, init) {
const res = await fetch(path, init)
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
return res.status === 204 ? null : res.json()
}fetch в Next.js
В Server Components fetch выполняется на сервере (API-ключи остаются секретными). В Client Components для серверных данных лучше TanStack Query, чем fetch в useEffect.
Коротко
fetch→Response; проверьтеres.ok, затемres.json().- JSON = текст;
stringifyпри отправке,parse(илиres.json()) при получении. URL+searchParamsдля параметров;AbortControllerдля отмены.