Fetch API — это встроенный в браузер способ делать HTTP‑запросы. Не нужно подключать библиотеки: просто вызываешь fetch(), передаёшь URL и (опционально) настройки, получаешь промис с ответом. Дальше разбираешь тело ответа (JSON, текст, Blob и т. д.) и работаешь с данными.
Минимальный GET‑запрос
Самый простой запрос:
fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error(`Ошибка HTTP: ${response.status}`); } return response.json(); // читаем тело как JSON }) .then(data => console.log(data)) .catch(err => console.error('Ошибка:', err));
Что тут важно:
response.ok— этоtrue, если статус в диапазоне 200–299. Если нет — значит, сервер вернул ошибку (404, 500 и т. п.), и её надо обработать.response.json()— асинхронный метод, который читает поток ответа и парсит JSON. Он тоже возвращает промис..catch()ловит и сетевые ошибки (нет сети, таймаут), и ошибки, которые ты сам выбросил (например, при!response.ok).
GET с параметрами (query string)
Если нужны параметры (например, ?page=1&limit=10), лучше собирать их через URLSearchParams:
<
const params = new URLSearchParams({ page: '1', limit: '10', search: 'hello' }); fetch(`https://api.example.com/items?${params}`) .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }) .then(data => console.log(data)) .catch(console.error);
Так меньше шансов ошибиться в ручном склеивании строки.
POST с JSON‑телом
Для отправки данных чаще всего используют JSON:
const payload = { name: 'Alice', age: 30 }; fetch('https://api.example.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }) .then(r => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }) .then(data => console.log('Создан пользователь:', data)) .catch(console.error);
Важные моменты:
- Обязательно указываем
Content-Type: application/json. - Тело запроса (
body) должно быть строкой — поэтомуJSON.stringify. - Сервер должен уметь принимать JSON и возвращать понятный ответ.
Обработка разных типов ответа
fetch не читает тело автоматически. Ты сам выбираешь, как его читать:
response.text()— обычный текст.response.json()— JSON (самый частый случай).response.blob()— бинарные данные (картинки, файлы).response.arrayBuffer()— низкоуровневые бинарные данные.
Пример с текстом:
fetch('https://example.com/page.html') .then(r => r.text()) .then(html => console.log(html)) .catch(console.error);
Пример с картинкой (Blob → URL):
fetch('https://example.com/image.png') .then(r => r.blob()) .then(blob => { const url = URL.createObjectURL(blob); document.querySelector('img').src = url; }) .catch(console.error);
async/await: более читаемый стиль
Вместо цепочки .then() можно писать через async/await:
async function getData() { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); console.log(data); } catch (err) { console.error('Ошибка запроса:', err); } } getData();
Это тот же самый код, но читается линейно: сначала запрос, потом проверка, потом разбор данных, потом обработка ошибок.
Частые ошибки и где «ломается»
- Забыли проверить
response.ok
Fetch считает успешным любой ответ от сервера, даже 404 и 500. Если не проверитьok, ты получишь промис, который «успешен», но внутри — ошибка сервера. - Пытаются сразу использовать данные без
awaitили.then()
response.json()возвращает промис. Нельзя написатьconst data = response.json();и сразу использоватьdata. Нужно либоawait response.json(), либо.then(json => ...). - Неправильный
Content-Typeили забылиJSON.stringify
Если отправить JSON без заголовкаContent-Type: application/json, сервер может не понять формат. Если забытьstringify, тело уйдёт как[object Object]— сервер не сможет распарсить. - CORS‑ошибки
Если делаешь запрос на другой домен, а сервер не разрешает CORS, браузер заблокирует ответ. Это видно в консоли как CORS‑ошибка. Тут проблема на стороне сервера: он должен отдавать правильные заголовки. - Нет обработки сетевых ошибок
Сетевые ошибки (нет интернета, таймаут, DNS) попадают только в.catch()или вtry/catchприawait. Если не ловить — ошибка просто «провалится» и ничего не будет видно.
Пример: форма отправки данных с обработкой ошибок
Допустим, есть простая форма:
<form id="userForm"> <input name="name" placeholder="Имя" required> <button type="submit">Отправить</button> </form> <div id="message"></div>
JS:
document.getElementById('userForm').addEventListener('submit', async (e) => { e.preventDefault(); const form = e.target; const messageBox = document.getElementById('message'); messageBox.textContent = ''; const data = { name: form.name.value.trim() }; try { const res = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!res.ok) { const errData = await res.json().catch(() => ({})); throw new Error(errData.message || `Ошибка сервера: ${res.status}`); } const result = await res.json(); messageBox.textContent = 'Пользователь создан: ' + result.name; } catch (err) { messageBox.textContent = 'Ошибка: ' + err.message; } });
Здесь:
- Отменяем стандартное поведение формы (
preventDefault). - Собираем данные, отправляем JSON.
- Проверяем
ok, читаем ошибку из тела, если она есть. - Показываем понятное сообщение пользователю.
Практический совет для PunBB и подобных проектов
Если ты интегрируешь PunBB с внешними сервисами (например, с Яндексом) и делаешь запросы из фронтенда:
- Держи URL API в конфиге или через прокси на бэкенде, чтобы не светить ключи и не зависеть от CORS.
- Для аутентификации часто используют куки или заголовки (например,
Authorization: Bearer ...). Куки отправляются автоматически, еслиcredentials: 'include'и сервер правильно настроен. - Пример с куками:
fetch('https://api.example.com/profile', { credentials: 'include' // отправлять куки домена }) .then(r => r.json()) .catch(console.error);
Без credentials: 'include' куки не уйдут — и авторизация не сработает.
