← Back to course

Fetch API

Fetch API

Вітаю знову.

У попередній лекції ти познайомився з local storage.

Local storage дозволяє JavaScript запамʼятовувати маленькі шматочки даних у браузері.

Дуже корисно.

Дуже практично.

Дуже в стилі: “браузер тепер має маленький блокнот.”

Сьогодні ми навчимо JavaScript отримувати дані ззовні сторінки.

І тут усе стає дуже реальним.

Вебсайти рідко тримають усі дані в одному JavaScript-файлі.

Вони часто завантажують дані з:

Щоб отримати ці дані, JavaScript використовує Fetch API.

Fetch дозволяє JavaScript попросити дані.

Браузер каже:

Я піду й принесу.

JavaScript відповідає:

Добре. Тільки, будь ласка, не повертайся з хаосом.

Іноді він повертається з даними.

Іноді — з помилкою.

Іноді — з чимось, що змушує тебе переосмислити життєві рішення.

Вітаю у веброзробці.

Що Ти Вивчиш

У цій лекції ти вивчиш:

Наприкінці цієї лекції твоя сторінка завантажуватиме дані з JSON-файлу й показуватиме їх на екрані.

Це величезний крок.

До цього більшість даних жила прямо в JavaScript.

Тепер JavaScript почне брати дані ззовні.

Як frontend-посланець.

У кращому взутті.

Що Таке Fetch?

fetch() — це JavaScript-функція для запиту даних.

Приклад:

fetch("products.json");

Це просить браузер завантажити файл products.json.

Але є важливий момент.

fetch() не повертає дані миттєво.

Це займає час.

Навіть якщо файл маленький.

Навіть якщо файл локальний.

Навіть якщо ти просиш дуже ввічливо.

Тому JavaScript сприймає fetch() як асинхронну операцію.

Це означає:

Почни запит зараз.
Продовжуй, коли результат буде готовий.

Це важливо.

Дуже важливо.

Інтернет не миттєвий.

Навіть коли прикидається таким.

Що Таке JSON?

JSON означає JavaScript Object Notation.

Це текстовий формат для зберігання й передачі даних.

Він схожий на JavaScript-обʼєкти.

Приклад JSON:

{
  "name": "JavaScript Course",
  "price": 49,
  "available": true
}

JSON всюди.

API використовують JSON.

Backend-и повертають JSON.

Frontend-застосунки читають JSON.

Твій майбутній backend на Java Spring Boot, швидше за все, теж буде віддавати JSON.

А frontend буде пити його як каву.

Обережно.

Бо поганий JSON приносить сум.

Створи Проєкт

Створи папку для цієї лекції:

mkdir javascript-lesson11
cd javascript-lesson11
touch index.html
touch script.js
touch products.json

Твій проєкт має виглядати так:

javascript-lesson11/
  index.html
  script.js
  products.json

Важливо:

У цій лекції не відкривай index.html напряму подвійним кліком.

Бо fetch() може неправильно завантажувати локальні файли через file://.

Використай локальний сервер.

Наприклад, з Caddy:

caddy file-server --listen :8080

Потім відкрий:

http://localhost:8080

Тепер браузер зможе нормально завантажити JSON-файл.

Без framework-дракона.

Просто маленький локальний сервер.

Цивілізовано.

Напиши JSON-Файл

Відкрий products.json і додай:

[
  {
    "id": 1,
    "name": "JavaScript Course",
    "price": 49,
    "available": true
  },
  {
    "id": 2,
    "name": "HTML Course",
    "price": 29,
    "available": true
  },
  {
    "id": 3,
    "name": "CSS Course",
    "price": 39,
    "available": false
  }
]

Це масив обʼєктів.

Кожен обʼєкт представляє один товар.

Кожен товар має:

Це дуже поширено.

Список товарів?

Масив обʼєктів.

Список користувачів?

Масив обʼєктів.

Список постів блогу?

Масив обʼєктів.

JavaScript бачить масиви обʼєктів всюди.

Як голубів на площі.

Напиши HTML

Відкрий index.html і додай:

<!DOCTYPE html>
<html lang="uk">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Fetch API</title>
</head>
<body>
  <h1>Fetch API</h1>

  <button id="loadButton">Завантажити Товари</button>

  <p id="message">Натисни кнопку, щоб завантажити товари.</p>

  <ul id="productList"></ul>

  <script src="script.js"></script>
</body>
</html>

Маємо:

Просто.

Чисто.

Готово отримувати дані.

Сторінка чекає.

Як офіціант з порожнім підносом.

Твій Перший Fetch

Відкрий script.js і додай:

const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");

async function loadProducts() {
  const response = await fetch("products.json");
  const products = await response.json();

  console.log(products);
}

loadButton.addEventListener("click", loadProducts);

Запусти локальний сервер:

caddy file-server --listen :8080

Відкрий:

http://localhost:8080

Натисни кнопку.

Відкрий консоль.

Ти маєш побачити масив товарів.

Вітаю.

JavaScript завантажив дані з JSON-файлу.

Це реально.

Маленьке реально.

Але реально.

Розуміння async і await

Подивись на цю функцію:

async function loadProducts() {
  const response = await fetch("products.json");
  const products = await response.json();

  console.log(products);
}

Слово async означає:

Ця функція виконує асинхронну роботу.

Слово await означає:

Почекай, поки ця операція завершиться, перед тим як рухатися далі.

Цей рядок:

const response = await fetch("products.json");

чекає, поки файл завантажиться.

Цей рядок:

const products = await response.json();

чекає, поки response перетвориться у JavaScript-дані.

Без await JavaScript не чекає.

А потім код стає розгубленим.

Як намагатися їсти пасту до того, як вона зварилася.

Технічно можливо.

Емоційно неправильно.

Що Таке response?

fetch() дає нам обʼєкт response.

Приклад:

const response = await fetch("products.json");

Response містить інформацію про запит.

Це ще не фінальні дані.

Щоб отримати JSON-дані, ми використовуємо:

const products = await response.json();

Це перетворює тіло response у JavaScript-дані.

Важливо:

response.json() також асинхронний.

Тому ми використовуємо await.

JavaScript потрібен момент, щоб прочитати й розпарсити JSON.

Навіть JavaScript потребує хвилинку.

Поважай процес.

Показ Товарів на Сторінці

Тепер покажемо товари в DOM.

Онови script.js:

const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");

async function loadProducts() {
  const response = await fetch("products.json");
  const products = await response.json();

  productListElement.innerHTML = "";

  for (const product of products) {
    const listItem = document.createElement("li");
    listItem.textContent = `${product.name} - €${product.price}`;
    productListElement.appendChild(listItem);
  }

  messageElement.textContent = `Завантажено товарів: ${products.length}.`;
}

loadButton.addEventListener("click", loadProducts);

Онови браузер.

Натисни кнопку.

Тепер товари зʼявляються на сторінці.

Що сталося?

JavaScript:

Це frontend development.

Дані приходять.

DOM оновлюється.

Користувач бачить контент.

Красиво.

Трохи драматично.

Але красиво.

Додай Статус Доступності

Тепер покажемо, чи товар доступний.

Заміни цей рядок:

listItem.textContent = `${product.name} - €${product.price}`;

на цей:

const status = product.available ? "Доступний" : "Недоступний";
listItem.textContent = `${product.name} - €${product.price} - ${status}`;

Повний цикл:

for (const product of products) {
  const listItem = document.createElement("li");
  const status = product.available ? "Доступний" : "Недоступний";

  listItem.textContent = `${product.name} - €${product.price} - ${status}`;
  productListElement.appendChild(listItem);
}

Це використовує ternary operator.

Він означає:

Якщо product.available true, використовуй "Доступний".
Інакше використовуй "Недоступний".

Коротко.

Корисно.

На початку трохи підозріло.

Але після практики дружньо.

Додай Loading State

Завантаження даних займає час.

Тому варто показати повідомлення про завантаження.

Онови loadProducts:

async function loadProducts() {
  messageElement.textContent = "Завантаження товарів...";
  productListElement.innerHTML = "";

  const response = await fetch("products.json");
  const products = await response.json();

  for (const product of products) {
    const listItem = document.createElement("li");
    const status = product.available ? "Доступний" : "Недоступний";

    listItem.textContent = `${product.name} - €${product.price} - ${status}`;
    productListElement.appendChild(listItem);
  }

  messageElement.textContent = `Завантажено товарів: ${products.length}.`;
}

Тепер після кліку сторінка каже:

Завантаження товарів...

Це хороший user experience.

Користувачі люблять знати, що щось відбувається.

Інакше вони клікають знову.

І знову.

І знову.

Потім твій застосунок отримує 47 запитів і починає плакати.

Обробка Помилок через try і catch

Іноді fetch не вдається.

Можливо, неправильна назва файлу.

Можливо, сервер не запущено.

Можливо, інтернет має філософську кризу.

Нам потрібно обробляти помилки.

Онови script.js:

const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");

async function loadProducts() {
  messageElement.textContent = "Завантаження товарів...";
  productListElement.innerHTML = "";

  try {
    const response = await fetch("products.json");
    const products = await response.json();

    for (const product of products) {
      const listItem = document.createElement("li");
      const status = product.available ? "Доступний" : "Недоступний";

      listItem.textContent = `${product.name} - €${product.price} - ${status}`;
      productListElement.appendChild(listItem);
    }

    messageElement.textContent = `Завантажено товарів: ${products.length}.`;
  } catch (error) {
    messageElement.textContent = "Не вдалося завантажити товари.";
    console.log(error);
  }
}

loadButton.addEventListener("click", loadProducts);

Тепер помилки не руйнують атмосферу мовчки.

Якщо щось піде не так, користувач побачить повідомлення.

А розробник побачить помилку в консолі.

Усі отримують інформацію.

Дуже здорово.

Дуже рідко.

Перевірка response.ok

Є ще один важливий момент.

fetch() не завжди кидає помилку при поганих HTTP-відповідях.

Наприклад, якщо файл не існує, сервер може повернути 404.

Тому варто перевірити:

if (!response.ok) {
  throw new Error("Не вдалося завантажити товари.");
}

Онови частину з fetch:

const response = await fetch("products.json");

if (!response.ok) {
  throw new Error("Не вдалося завантажити товари.");
}

const products = await response.json();

Повна функція:

async function loadProducts() {
  messageElement.textContent = "Завантаження товарів...";
  productListElement.innerHTML = "";

  try {
    const response = await fetch("products.json");

    if (!response.ok) {
      throw new Error("Не вдалося завантажити товари.");
    }

    const products = await response.json();

    for (const product of products) {
      const listItem = document.createElement("li");
      const status = product.available ? "Доступний" : "Недоступний";

      listItem.textContent = `${product.name} - €${product.price} - ${status}`;
      productListElement.appendChild(listItem);
    }

    messageElement.textContent = `Завантажено товарів: ${products.length}.`;
  } catch (error) {
    messageElement.textContent = "Не вдалося завантажити товари.";
    console.log(error);
  }
}

Тепер код сильніший.

Не безсмертний.

Але сильніший.

Як кава після другої чашки.

Додай Кращі Стилі

Зробімо сторінку красивішою.

Онови index.html:

<!DOCTYPE html>
<html lang="uk">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Fetch API</title>

  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 800px;
      margin: 60px auto;
      padding: 0 24px;
      background-color: #f3f4f6;
      color: #111827;
    }

    .card {
      background-color: white;
      padding: 24px;
      border: 2px solid #e5e7eb;
      border-radius: 18px;
    }

    h1 {
      font-size: 42px;
    }

    button {
      background-color: #2563eb;
      color: white;
      border: none;
      padding: 14px 20px;
      border-radius: 999px;
      font-weight: 700;
      cursor: pointer;
    }

    button:hover {
      background-color: #1d4ed8;
    }

    .message {
      margin-top: 20px;
      font-size: 18px;
      font-weight: 700;
    }

    .products {
      list-style: none;
      padding: 0;
      margin-top: 20px;
      display: grid;
      gap: 12px;
    }

    .product {
      padding: 16px;
      border: 2px solid #e5e7eb;
      border-radius: 14px;
      background-color: #f9fafb;
    }

    .product strong {
      display: block;
      font-size: 22px;
      margin-bottom: 6px;
    }

    .available {
      color: #166534;
      font-weight: 700;
    }

    .unavailable {
      color: #991b1b;
      font-weight: 700;
    }
  </style>
</head>
<body>
  <h1>Fetch API</h1>

  <div class="card">
    <button id="loadButton">Завантажити Товари</button>

    <p id="message" class="message">Натисни кнопку, щоб завантажити товари.</p>

    <ul id="productList" class="products"></ul>
  </div>

  <script src="script.js"></script>
</body>
</html>

Тепер онови показ товарів у script.js:

for (const product of products) {
  const listItem = document.createElement("li");
  const status = product.available ? "Доступний" : "Недоступний";
  const statusClass = product.available ? "available" : "unavailable";

  listItem.className = "product";

  listItem.innerHTML = `
    <strong>${product.name}</strong>
    <span>Ціна: €${product.price}</span><br>
    <span class="${statusClass}">${status}</span>
  `;

  productListElement.appendChild(listItem);
}

Тепер товари виглядають як картки.

Набагато краще.

Менше “сирий HTML з печери.”

Більше “маленький frontend-застосунок для початківців.”

Прогрес.

Повний JavaScript Код

Ось повний script.js:

const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");

async function loadProducts() {
  messageElement.textContent = "Завантаження товарів...";
  productListElement.innerHTML = "";

  try {
    const response = await fetch("products.json");

    if (!response.ok) {
      throw new Error("Не вдалося завантажити товари.");
    }

    const products = await response.json();

    for (const product of products) {
      const listItem = document.createElement("li");
      const status = product.available ? "Доступний" : "Недоступний";
      const statusClass = product.available ? "available" : "unavailable";

      listItem.className = "product";

      listItem.innerHTML = `
        <strong>${product.name}</strong>
        <span>Ціна: €${product.price}</span><br>
        <span class="${statusClass}">${status}</span>
      `;

      productListElement.appendChild(listItem);
    }

    messageElement.textContent = `Завантажено товарів: ${products.length}.`;
  } catch (error) {
    messageElement.textContent = "Не вдалося завантажити товари.";
    console.log(error);
  }
}

loadButton.addEventListener("click", loadProducts);

Тепер у тебе є:

Це вже серйозний beginner-проєкт.

Маленький.

Але серйозний.

Як маленьке espresso.

Типові Помилки

Відкривати index.html Напряму

Неправильно:

file:///home/user/javascript-lesson11/index.html

Краще:

caddy file-server --listen :8080

Потім відкрий:

http://localhost:8080

Fetch часто потребує сервер.

Навіть маленький локальний.

Не бийся з браузером.

У нього правила.

Багато правил.

Забути await

Неправильно:

const response = fetch("products.json");
const products = response.json();

Правильно:

const response = await fetch("products.json");
const products = await response.json();

Без await у тебе ще немає даних.

У тебе є promise.

Promise — це не результат.

Це майбутній результат.

Як замовлення піци.

Чек — це не піца.

Знову піца.

Але працює.

Забути response.json()

Неправильно:

const products = await response;

Правильно:

const products = await response.json();

Response — це не фінальні дані.

Треба перетворити його в JSON.

JavaScript потужний.

Але йому все одно потрібні інструкції.

Неправильний Шлях до Файлу

Якщо файл називається:

products.json

тоді fetch:

fetch("products.json")

Якщо файл у папці:

data/products.json

тоді fetch:

fetch("data/products.json")

Шляхи мають збігатися.

JavaScript не буде шукати по папках як детектив.

Він просто впаде.

Тихо.

Як кіт, який скидає щось зі столу.

Практика

Створи новий JSON-файл з назвою students.json.

Додай це:

[
  {
    "name": "Anna",
    "level": "Beginner"
  },
  {
    "name": "Marco",
    "level": "Intermediate"
  },
  {
    "name": "Viktor",
    "level": "Beginner"
  }
]

Створи кнопку, яка завантажує студентів.

Покажи кожного студента на сторінці.

Приклад результату:

Anna - Beginner
Marco - Intermediate
Viktor - Beginner

Бонус:

Покажи, скільки студентів було завантажено.

Дуже схоже на товари.

Інші дані.

Та сама схема.

Так працює програмування.

Вивчи один шаблон.

Використовуй його всюди.

Роби вигляд, що було складно.

Дуже професійно.

Мінічелендж

Створи маленький каталог курсів.

Створи courses.json з:

Потім використай fetch(), щоб завантажити курси й показати їх як картки.

Кожна картка має показувати:

Бонус:

Показуй тільки доступні курси.

Це означає, що можна використати:

if (course.available) {
  // show course
}

Цей челендж поєднує:

Коротко кажучи, JavaScript стає маленькою фабрикою вебзастосунків.

Вітаю.

І ще: обережно.

Фабрикам потрібна організація.

Підсумок

Сьогодні ти вивчив:

Це величезний крок.

Твій JavaScript тепер може говорити із зовнішніми даними.

Не тільки зі змінними.

Не тільки з local storage.

Справжні файли.

Потім API.

Потім сервери.

Зовнішній світ.

JavaScript відкрив вікно.

Сподіваюсь, не неправильне.

Наступна Лекція

У наступній лекції ми створимо маленький фінальний проєкт.

Ми поєднаємо:

Справжній JavaScript-проєкт для початківців.

Не величезний.

Не страшний.

Але повний.

Final boss наближається.

Принеси каву.