Урок 5 из 8 · 25 мин · обновлено

Данные с сервера: API, JSON и запросы fetch

Как frontend получает данные от backend: что такое API и JSON, как отправить запрос с помощью fetch, показать данные на странице и не забыть про загрузку и ошибки.

API: договорённость с backend

Меню «Зерна» меняется каждый сезон, и цены хранятся в базе на сервере. Frontend не знает их заранее — он запрашивает их у backend через API.

API — договорённость: «по адресу /api/menu сервер отдаёт список напитков в таком-то виде». Backend-разработчик описывает API, frontend-разработчик его использует. Если формат меняется, обе стороны должны знать об этом заранее.

JSON

Данные обычно приходят в формате JSON — текст, похожий на объекты JavaScript:

JSON
[
  { "id": 1, "name": "Капучино", "price": 220, "available": true },
  { "id": 2, "name": "Латте", "price": 250, "available": true },
  { "id": 3, "name": "Раф", "price": 290, "available": false }
]

Это массив из трёх напитков. У каждого — номер, название, цена и признак, есть ли он сейчас в наличии.

Запрос fetch

Чтобы получить данные, используют встроенную функцию fetch:

JavaScript
async function loadMenu() {
  const response = await fetch('/api/menu');
  const drinks = await response.json();
  console.log(drinks.length);
}

loadMenu();
Результат
3
  • fetch отправляет запрос на сервер;
  • await ждёт ответа и не замораживает страницу — пользователь может прокручивать и нажимать;
  • response.json() превращает текст ответа в массив объектов.

Показываем меню

Теперь выведем напитки на страницу, пропуская те, что закончились:

JavaScript
async function loadMenu() {
  const response = await fetch('/api/menu');
  const drinks = await response.json();
  const list = document.querySelector('.menu');

  for (const drink of drinks.filter(d => d.available)) {
    const item = document.createElement('li');
    item.textContent = `${drink.name} — ${drink.price} ₽`;
    list.append(item);
  }
}

На странице появятся две строки: «Капучино — 220 ₽» и «Латте — 250 ₽».

Мы используем textContent, а не innerHTML. Если в названии от сервера по ошибке или злому умыслу окажется HTML-код, textContent покажет его как текст, а innerHTML встроит в страницу. Почему это опасно, разобрано в уроке об уязвимостях курса «Специалист по кибербезопасности».

Загрузка и ошибки

У каждого запроса три состояния, и интерфейс должен показать все:

Состояние Что видит пользователь
Загрузка «Загружаем меню…» или заготовки карточек
Успех Список напитков
Ошибка «Не получилось загрузить меню» и кнопка «Попробовать снова»
JavaScript
async function loadMenu() {
  const status = document.querySelector('.status');
  status.textContent = 'Загружаем меню…';
  try {
    const response = await fetch('/api/menu');
    if (!response.ok) throw new Error(`Код ответа ${response.status}`);
    const drinks = await response.json();
    status.textContent = '';
    // показываем drinks, как выше
  } catch (error) {
    status.textContent = 'Не получилось загрузить меню. Проверьте интернет и попробуйте снова.';
  }
}

Новички часто делают только «успех» — и пользователь с медленным интернетом видит пустую страницу. Посмотреть, какие запросы отправляет страница и что приходит в ответ, можно во вкладке Network инструментов разработчика. Подробнее о работе с API — в курсе «Postman и API».

Попробуйте сами. Сервер «Зерна» по адресу /api/orders/42 отдаёт заказ: { "id": 42, "status": "cooking", "minutes": 4 }. Напишите функцию, которая показывает на странице «Заказ №42 готовится, осталось 4 мин», а при ошибке — понятное сообщение.

Пример ответа
JavaScript
async function showOrder(id) {
  const box = document.querySelector('.order-status');
  box.textContent = 'Проверяем заказ…';
  try {
    const response = await fetch(`/api/orders/${id}`);
    if (!response.ok) throw new Error(response.status);
    const order = await response.json();
    box.textContent = order.status === 'cooking'
      ? `Заказ №${order.id} готовится, осталось ${order.minutes} мин`
      : `Заказ №${order.id} готов`;
  } catch (error) {
    box.textContent = 'Не получилось узнать статус заказа. Попробуйте обновить страницу.';
  }
}

showOrder(42);

Мини-тест · 4 вопроса

Проверьте себя

Выберите вариант, который кажется вам правильным. В конце вы увидите свой результат и объяснение к каждому вопросу.

  1. Что такое API в веб-разработке?

  2. Для чего нужен await перед fetch?

  3. Сервер не ответил. Что должен увидеть пользователь?

  4. Почему название блюда от сервера лучше вставлять через textContent, а не innerHTML?