Пошук уроків, статей та іншого контенту
Виконаєте HTTP-запити через fetch, перевірите статус відповіді, прочитаєте дані та обробите мережеві помилки.
fetchfetch — вбудований JavaScript API для виконання HTTP-запитів. Він повертає Promise, який виконується об’єктом Response.
Найпростіший GET-запит:
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error("Помилка запиту:", error);
});Запит виконується асинхронно: JavaScript не блокує виконання іншого коду, поки очікується відповідь сервера.
ResponseРезультат fetch — це не одразу дані сервера, а об’єкт Response.
Він містить, зокрема:
status — числовий HTTP-статус, наприклад 200 або 404;
ok — true, якщо статус перебуває в діапазоні 200–299;
statusText — текстовий опис статусу;
headers — заголовки відповіді;
json() — метод для читання JSON;
text() — метод для читання звичайного тексту.
Важливо: fetch не відхиляє Promise автоматично для HTTP-помилок.
Наприклад, відповідь зі статусом 404 зазвичай успішно потрапить у перший обробник .then(). Тому статус потрібно перевіряти самостійно:
fetch("https://jsonplaceholder.typicode.com/todos/99999")
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log("Отримані дані:", data);
})
.catch((error) => {
console.error(error.message);
});Методи читання тіла відповіді також є асинхронними й повертають Promise.
Для JSON-відповіді використовуйте response.json():
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await response.json();
console.log(data);Метод json():
читає тіло відповіді;
аналізує JSON;
повертає JavaScript-значення — об’єкт, масив, рядок, число тощо.
Якщо сервер повертає звичайний текст, використовуйте response.text():
const response = await fetch("https://example.com");
const html = await response.text();
console.log(html);Тіло відповіді можна прочитати лише один раз. Наприклад, після response.json() не можна повторно викликати response.text() для того самого об’єкта Response.
async і awaitСинтаксис async/await зазвичай робить асинхронний код зрозумілішим.
async function loadTodo() {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const todo = await response.json();
console.log("Завдання:", todo.title);
console.log("Виконано:", todo.completed);
}
loadTodo().catch((error) => {
console.error("Не вдалося завантажити завдання:", error.message);
});await можна використовувати лише всередині async-функції або в середовищах, які підтримують top-level await.
fetch відхиляє Promise, якщо запит не вдалося виконати через мережеву проблему, наприклад:
немає підключення до інтернету;
DNS-ім’я не вдалося знайти;
з’єднання було перервано;
запит заблокувала політика CORS у браузері.
Однак HTTP-статуси 404, 500 та інші самі по собі не вважаються мережевими помилками. Їх потрібно перевіряти через response.ok.
Зручний шаблон має обробляти обидва типи помилок:
async function fetchJson(url, options = {}) {
let response;
try {
response = await fetch(url, options);
} catch (error) {
throw new Error(`Мережева помилка: ${error.message}`);
}
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
try {
return await response.json();
} catch {
throw new Error("Сервер повернув некоректний JSON");
}
}
async function main() {
try {
const todo = await fetchJson(
"https://jsonplaceholder.typicode.com/todos/1"
);
console.log(todo);
} catch (error) {
console.error(error.message);
}
}
main();fetchДругий аргумент fetch — об’єкт налаштувань запиту.
Найчастіше використовують такі властивості:
method — HTTP-метод;
headers — заголовки;
body — тіло запиту;
signal — сигнал для скасування запиту.
За замовчуванням fetch виконує GET-запит:
const response = await fetch("/api/users");Це еквівалентно:
const response = await fetch("/api/users", {
method: "GET"
});Для надсилання JSON потрібно:
встановити метод POST;
додати заголовок Content-Type: application/json;
перетворити JavaScript-об’єкт у JSON через JSON.stringify().
async function createTodo() {
const newTodo = {
title: "Вивчити fetch",
completed: false,
userId: 1
};
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos",
{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newTodo)
}
);
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const createdTodo = await response.json();
console.log("Створене завдання:", createdTodo);
}
createTodo().catch((error) => {
console.error("Не вдалося створити завдання:", error.message);
});JSON.stringify() перетворює JavaScript-об’єкт на текстовий JSON. Без цього сервер може не розпізнати тіло запиту.
Для оновлення або видалення ресурсів використовують інші HTTP-методи.
PUT зазвичай використовують для повної заміни ресурсу:
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1",
{
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
id: 1,
title: "Оновлене завдання",
completed: true,
userId: 1
})
}
);PATCH призначений для часткового оновлення:
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1",
{
method: "PATCH",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
completed: true
})
}
);DELETE видаляє ресурс:
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1",
{
method: "DELETE"
}
);
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
console.log("Ресурс видалено");Деякі сервери після DELETE повертають порожнє тіло. У такому випадку не потрібно безумовно викликати response.json().
Параметри запиту додають до URL після символу ?. Для безпечного формування URL зручно використовувати URLSearchParams:
async function loadTodos(userId) {
const params = new URLSearchParams({
userId: String(userId),
_limit: "5"
});
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos?${params}`
);
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
return response.json();
}
loadTodos(1)
.then((todos) => {
console.log("Кількість завдань:", todos.length);
})
.catch((error) => {
console.error(error.message);
});URLSearchParams правильно кодує спеціальні символи в значеннях параметрів.
Іноді запит потрібно скасувати:
користувач залишив сторінку;
новий пошуковий запит замінив попередній;
минув допустимий час очікування.
Для цього використовують AbortController:
async function loadWithTimeout(url, timeout = 3000) {
const controller = new AbortController();
const timerId = setTimeout(() => {
controller.abort();
}, timeout);
try {
const response = await fetch(url, {
signal: controller.signal
});
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === "AbortError") {
throw new Error("Запит перевищив допустимий час очікування");
}
throw error;
} finally {
clearTimeout(timerId);
}
}
loadWithTimeout("https://jsonplaceholder.typicode.com/todos/1")
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error.message);
});Метод abort() скасовує запит, а fetch завершується помилкою з ім’ям AbortError.
Нижче наведено приклад функцій для отримання, створення та видалення завдань:
const API_URL = "https://jsonplaceholder.typicode.com/todos";
async function requestJson(url, options = {}) {
let response;
try {
response = await fetch(url, options);
} catch (error) {
throw new Error(`Не вдалося виконати запит: ${error.message}`);
}
if (!response.ok) {
throw new Error(`Сервер повернув статус ${response.status}`);
}
const contentType = response.headers.get("content-type") || "";
if (!contentType.includes("application/json")) {
throw new Error("Відповідь сервера не є JSON");
}
return response.json();
}
async function getTodo(id) {
return requestJson(`${API_URL}/${id}`);
}
async function createTodo(title) {
return requestJson(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
title,
completed: false,
userId: 1
})
});
}
async function deleteTodo(id) {
const response = await fetch(`${API_URL}/${id}`, {
method: "DELETE"
});
if (!response.ok) {
throw new Error(`Не вдалося видалити завдання: ${response.status}`);
}
}
async function main() {
try {
const todo = await getTodo(1);
console.log("Отримане завдання:", todo);
const createdTodo = await createTodo("Повторити HTTP-методи");
console.log("Створене завдання:", createdTodo);
await deleteTodo(createdTodo.id);
console.log("Завдання видалено");
} catch (error) {
console.error("Помилка:", error.message);
}
}
main();Цей приклад можна виконати в браузері або в Node.js 18 і новіших версіях, де fetch доступний глобально.
Для більшості API-клієнтів зручно дотримуватися такого алгоритму:
Сформувати URL і параметри.
Вказати HTTP-метод.
Додати заголовки.
Серіалізувати тіло запиту, якщо потрібно.
Виконати fetch.
Обробити мережеву помилку.
Перевірити response.ok.
Прочитати тіло відповіді потрібним методом.
Обробити дані або показати помилку користувачу.
response.okНеправильно:
const data = await fetch("/api/items").then((response) => response.json());Такий код може спробувати прочитати помилкову відповідь так, ніби це успішний результат.
Краще:
const response = await fetch("/api/items");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const data = await response.json();await перед response.json()Неправильно:
const response = await fetch("/api/items");
const data = response.json();
console.log(data); // PromiseПравильно:
const response = await fetch("/api/items");
const data = await response.json();
console.log(data);Неправильно:
const response = await fetch("/api/items");
const data = await response.json();
const text = await response.text();Тіло відповіді вже було прочитане через json(). Потрібно вибрати один спосіб читання.
JSON.stringifyНеправильно:
await fetch("/api/items", {
method: "POST",
body: {
title: "Нове завдання"
}
});Правильно:
await fetch("/api/items", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
title: "Нове завдання"
})
});Перевірка response.ok не замінює try...catch. Навіть коректний URL може бути недоступним через проблеми з мережею.
try {
const response = await fetch("/api/items");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
} catch (error) {
console.error("Запит не виконано:", error.message);
}fetch виконує HTTP-запити та повертає Promise.
Результатом fetch є об’єкт Response, а не готові дані.
Для JSON використовуйте response.json().
Для тексту використовуйте response.text().
HTTP-помилки потрібно перевіряти через response.ok або response.status.
Мережеві помилки обробляються через try...catch.
JSON у тілі запиту потрібно серіалізувати через JSON.stringify().
Заголовок Content-Type повідомляє серверу формат тіла запиту.
AbortController дає змогу скасувати запит.
Тіло відповіді можна прочитати лише один раз.