Fetch API
JavaScript Fetch API — поштове відділення з полицями для сортування листів та посилок
У попередніх двох уроках ми вивчили Promises та async/await. Тепер застосуємо ці знання на практиці — навчимося робити HTTP-запити до серверів. fetch() — це вбудований API браузера для мережевих запитів, який повертає Promise.
Що таке API?
API (Application Programming Interface) — це набір правил, за якими програми спілкуються між собою. Коли ти відкриваєш сайт, браузер робить запит до сервера і отримує відповідь — це і є робота з API.
REST API — найпоширеніший тип API для вебзастосунків. Він використовує стандартні HTTP-методи:
| Метод | Дія | Приклад |
|---|---|---|
| GET | Отримати дані | Список користувачів |
| POST | Створити нові дані | Реєстрація нового користувача |
| PUT/PATCH | Оновити дані | Зміна імені користувача |
| DELETE | Видалити дані | Видалення акаунту |
Формат JSON
Сервери зазвичай повертають дані у форматі JSON (JavaScript Object Notation). Він виглядає як JavaScript об'єкт, але є текстовим рядком:
// JSON — це текст
const jsonString = '{"name": "Олексій", "age": 25, "isStudent": true}';
// Перетворення JSON → об'єкт
const user = JSON.parse(jsonString);
console.log(user.name); // "Олексій"
// Перетворення об'єкт → JSON
const backToJson = JSON.stringify(user);
console.log(backToJson); // '{"name":"Олексій","age":25,"isStudent":true}'
fetch() — основи
fetch(url) робить HTTP-запит і повертає Promise:
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json()) // парсимо JSON
.then((user) => console.log(user))
.catch((error) => console.error("Помилка:", error));
З async/await (рекомендований спосіб):
async function getUser() {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
const user = await response.json();
console.log(user);
}
fetch() повертає не дані, а об'єкт Response. Щоб отримати дані, потрібно викликати .json() (для JSON) або .text() (для тексту). Ці методи теж повертають Promise, тому потрібен ще один await.
Об'єкт Response
fetch() повертає об'єкт Response з корисними властивостями:
async function checkResponse() {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
console.log(response.ok); // true (статус 200-299)
console.log(response.status); // 200
console.log(response.statusText); // "OK"
console.log(response.headers); // Headers об'єкт
const data = await response.json(); // тіло відповіді
console.log(data);
}
| Властивість | Опис |
|---|---|
response.ok | true якщо статус 200-299 |
response.status | HTTP статус код (200, 404, 500...) |
response.json() | Парсить тіло як JSON (повертає Promise) |
response.text() | Повертає тіло як текст (Promise) |
GET-запити
GET — найпростіший запит. Він використовується для отримання даних:
// Отримати список всіх користувачів
async function getUsers() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) {
throw new Error(`Помилка: ${response.status}`);
}
const users = await response.json();
console.log(`Отримано ${users.length} користувачів`);
users.forEach((user) => {
console.log(`- ${user.name} (${user.email})`);
});
} catch (error) {
console.error("Не вдалося завантажити:", error.message);
}
}
getUsers();
GET з параметрами (query string)
// Отримати пости конкретного користувача
async function getUserPosts(userId) {
const url = `https://jsonplaceholder.typicode.com/posts?userId=${userId}`;
const response = await fetch(url);
const posts = await response.json();
return posts;
}
// Або через URLSearchParams
async function searchPosts(params) {
const query = new URLSearchParams(params).toString();
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts?${query}`
);
return response.json();
}
// Використання
const posts = await searchPosts({ userId: 1, _limit: 5 });
POST-запити
POST використовується для створення нових даних. Потрібно вказати метод, заголовки та тіло:
async function createPost(post) {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(post),
});
if (!response.ok) {
throw new Error(`Помилка: ${response.status}`);
}
const newPost = await response.json();
console.log("Створено пост:", newPost);
return newPost;
} catch (error) {
console.error("Не вдалося створити:", error.message);
}
}
// Використання
createPost({
title: "Мій перший пост",
body: "Це тестовий пост з JavaScript!",
userId: 1,
});
Не забувай Content-Type: application/json у заголовках при відправці JSON! Без цього сервер може не розпізнати формат даних. Також обов'язково використовуй JSON.stringify() для тіла запиту.
PUT та PATCH
PUT — повна заміна ресурсу, PATCH — часткове оновлення:
// PUT — замінює весь ресурс
async function updatePost(id, updatedPost) {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${id}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(updatedPost),
}
);
return response.json();
}
// PATCH — оновлює окремі поля
async function patchPost(id, changes) {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${id}`,
{
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(changes),
}
);
return response.json();
}
// Використання
await updatePost(1, { title: "Новий заголовок", body: "Новий текст", userId: 1 });
await patchPost(1, { title: "Оновлений заголовок" });
DELETE-запити
async function deletePost(id) {
try {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${id}`,
{ method: "DELETE" }
);
if (response.ok) {
console.log(`Пост ${id} видалено`);
}
} catch (error) {
console.error("Помилка видалення:", error.message);
}
}
await deletePost(1);
Обробка помилок
fetch() має важливу особливість: він не кидає помилку при HTTP-помилках (404, 500). Помилка виникає лише при мережевих проблемах:
// ❌ fetch не кидає помилку при 404!
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users/999");
const data = await response.json(); // спроба парсити порожню відповідь
console.log(data); // {} — порожній об'єкт
} catch (error) {
// Цей catch НЕ спрацює при 404
}
// ✅ Правильна обробка помилок
async function safeFetch(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
if (error.name === "TypeError") {
// Мережева помилка (немає інтернету, сервер недоступний)
console.error("Мережева помилка:", error.message);
} else {
// HTTP помилка або помилка парсингу
console.error("Помилка запиту:", error.message);
}
return null;
}
}
Завжди перевіряй response.ok перед обробкою даних! fetch() вважає запит успішним навіть при статусах 404 або 500. Помилку кидає лише при проблемах з мережею.
JSONPlaceholder — безкоштовне API для практики
JSONPlaceholder — це безкоштовне fake REST API для тестування. Доступні ресурси:
| Ресурс | URL |
|---|---|
| Користувачі | /users |
| Пости | /posts |
| Коментарі | /comments |
| Альбоми | /albums |
| Фото | /photos |
| Todos | /todos |
const BASE_URL = "https://jsonplaceholder.typicode.com";
async function demo() {
// Список користувачів
const users = await safeFetch(`${BASE_URL}/users`);
console.log("Користувачі:", users?.length);
// Пости першого користувача
const posts = await safeFetch(`${BASE_URL}/posts?userId=1`);
console.log("Пости:", posts?.length);
// Конкретний пост
const post = await safeFetch(`${BASE_URL}/posts/1`);
console.log("Пост:", post?.title);
}
demo();
Практика: відображення даних на сторінці
Ось повний приклад — завантаження користувачів та відображення у DOM:
async function displayUsers() {
const container = document.getElementById("users");
container.innerHTML = "<p>Завантаження...</p>";
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users");
if (!response.ok) {
throw new Error(`Помилка: ${response.status}`);
}
const users = await response.json();
container.innerHTML = users
.map(
(user) => `
<div class="user-card">
<h3>${user.name}</h3>
<p>Email: ${user.email}</p>
<p>Місто: ${user.address.city}</p>
</div>
`
)
.join("");
} catch (error) {
container.innerHTML = `<p class="error">Помилка: ${error.message}</p>`;
}
}
displayUsers();
CORS — коротко
Коли ти робиш запит з одного домену на інший (наприклад, з localhost на api.example.com), браузер перевіряє заголовки CORS (Cross-Origin Resource Sharing). Якщо сервер не дозволяє запити з твого домену — отримаєш помилку.
Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000'
has been blocked by CORS policy
CORS — це механізм безпеки браузера. Він захищає користувачів від шкідливих сайтів, що намагаються робити запити від їхнього імені. JSONPlaceholder дозволяє CORS для всіх, тому проблем з ним не буде. У реальних проектах CORS налаштовує backend-розробник.
Підсумок
fetch(url)робить HTTP-запит, повертає Promise з Responseresponse.json()парсить відповідь як JSON (теж Promise)- GET — отримати, POST — створити, PUT/PATCH — оновити, DELETE — видалити
- POST/PUT/PATCH потребують
method,headersтаbody: JSON.stringify(data) response.okперевіряє, чи статус 200-299 (fetch не кидає помилку при 404/500!)- JSONPlaceholder — безкоштовне API для практики та тестування
Що далі?
У наступному уроці — ES6 Modules: import/export, організація коду у модулі, named та default exports.
Корисні посилання:
- javascript.info: Fetch — повний підручник (українською)
- MDN: Fetch API — документація
- JSONPlaceholder — безкоштовне REST API для тестування
- HTTP Status Codes — довідник HTTP статус-кодів