← Back to course

Fetch API

Fetch API

Witaj z powrotem.

W poprzedniej lekcji poznałeś local storage.

Local storage pozwala JavaScript zapamiętywać małe kawałki danych w przeglądarce.

Bardzo użyteczne.

Bardzo praktyczne.

Bardzo w stylu: “przeglądarka ma teraz mały notes.”

Dzisiaj nauczymy JavaScript pobierać dane spoza strony.

I tutaj robi się naprawdę poważnie.

Strony internetowe rzadko trzymają wszystkie dane w jednym pliku JavaScript.

Często ładują dane z:

Do pobierania tych danych JavaScript używa Fetch API.

Fetch pozwala JavaScript poprosić o dane.

Przeglądarka mówi:

Pójdę i je przyniosę.

JavaScript odpowiada:

Dobrze. Tylko proszę, nie wracaj z chaosem.

Czasami wraca z danymi.

Czasami wraca z błędem.

Czasami wraca z czymś, co każe ci przemyśleć swoje decyzje życiowe.

Witaj w web developmencie.

Czego Się Nauczysz

W tej lekcji nauczysz się:

Na końcu tej lekcji twoja strona załaduje dane z pliku JSON i pokaże je na ekranie.

To ogromny krok.

Do tej pory większość danych mieszkała bezpośrednio w JavaScript.

Teraz JavaScript zacznie pobierać dane z zewnątrz.

Jak frontendowy posłaniec.

W lepszych butach.

Czym Jest Fetch?

fetch() to funkcja JavaScript używana do pobierania danych.

Przykład:

fetch("products.json");

To prosi przeglądarkę o załadowanie pliku products.json.

Ale jest jedna ważna rzecz.

fetch() nie zwraca danych natychmiast.

To zajmuje czas.

Nawet jeśli plik jest mały.

Nawet jeśli plik jest lokalny.

Nawet jeśli poprosisz bardzo grzecznie.

Dlatego JavaScript traktuje fetch() jako operację asynchroniczną.

To znaczy:

Zacznij żądanie teraz.
Kontynuuj, kiedy wynik będzie gotowy.

To ważne.

Bardzo ważne.

Internet nie jest natychmiastowy.

Nawet kiedy udaje, że jest.

Czym Jest JSON?

JSON oznacza JavaScript Object Notation.

To tekstowy format używany do przechowywania i przesyłania danych.

Wygląda podobnie do obiektów JavaScript.

Przykład JSON:

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

JSON jest wszędzie.

API używają JSON.

Backendy zwracają JSON.

Aplikacje frontendowe czytają JSON.

Twój przyszły backend Java Spring Boot prawdopodobnie będzie wysyłał JSON.

A frontend będzie go pił jak kawę.

Ostrożnie.

Bo zły JSON powoduje smutek.

Utwórz Projekt

Utwórz folder dla tej lekcji:

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

Twój projekt powinien wyglądać tak:

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

Ważne:

W tej lekcji nie otwieraj index.html bezpośrednio przez podwójne kliknięcie.

Bo fetch() może nie załadować poprawnie lokalnych plików z file://.

Użyj lokalnego serwera.

Na przykład z Caddy:

caddy file-server --listen :8080

Potem otwórz:

http://localhost:8080

Teraz przeglądarka może poprawnie załadować plik JSON.

Bez smoka frameworków.

Tylko mały lokalny serwer.

Cywilizowanie.

Napisz Plik JSON

Otwórz products.json i dodaj:

[
  {
    "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
  }
]

To jest tablica obiektów.

Każdy obiekt reprezentuje jeden produkt.

Każdy produkt ma:

To jest bardzo częste.

Lista produktów?

Tablica obiektów.

Lista użytkowników?

Tablica obiektów.

Lista wpisów blogowych?

Tablica obiektów.

JavaScript widzi tablice obiektów wszędzie.

Jak gołębie na rynku.

Napisz HTML

Otwórz index.html i dodaj:

<!DOCTYPE html>
<html lang="pl">
<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">Wczytaj Produkty</button>

  <p id="message">Kliknij przycisk, aby wczytać produkty.</p>

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

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

Mamy:

Prosto.

Czysto.

Gotowe na dane.

Strona czeka.

Jak kelner z pustą tacą.

Twój Pierwszy Fetch

Otwórz script.js i dodaj:

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);

Uruchom lokalny serwer:

caddy file-server --listen :8080

Otwórz:

http://localhost:8080

Kliknij przycisk.

Otwórz konsolę.

Powinieneś zobaczyć tablicę produktów.

Gratulacje.

JavaScript załadował dane z pliku JSON.

To jest prawdziwe.

Małe prawdziwe.

Ale prawdziwe.

Zrozumieć async i await

Spójrz na tę funkcję:

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

  console.log(products);
}

Słowo async oznacza:

Ta funkcja wykonuje pracę asynchroniczną.

Słowo await oznacza:

Poczekaj, aż ta operacja się zakończy, zanim przejdziesz dalej.

Ta linia:

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

czeka, aż plik zostanie załadowany.

Ta linia:

const products = await response.json();

czeka, aż response zostanie zamieniony na dane JavaScript.

Bez await JavaScript nie czeka.

A potem kod robi się zdezorientowany.

Jak próba jedzenia makaronu, zanim się ugotuje.

Technicznie możliwe.

Emocjonalnie złe.

Czym Jest response?

fetch() daje nam obiekt response.

Przykład:

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

Response zawiera informacje o żądaniu.

To jeszcze nie są końcowe dane.

Aby dostać dane JSON, używamy:

const products = await response.json();

To zamienia ciało response na dane JavaScript.

Ważne:

response.json() też jest asynchroniczne.

Dlatego używamy await.

JavaScript potrzebuje chwili, żeby przeczytać i sparsować JSON.

Nawet JavaScript potrzebuje momentu.

Szanuj proces.

Wyświetlanie Produktów na Stronie

Teraz pokażmy produkty w DOM.

Zaktualizuj 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 = `Wczytano ${products.length} produkty/produktów.`;
}

loadButton.addEventListener("click", loadProducts);

Odśwież przeglądarkę.

Kliknij przycisk.

Teraz produkty pojawiają się na stronie.

Co się stało?

JavaScript:

To jest frontend development.

Dane przychodzą.

DOM się aktualizuje.

Użytkownik widzi treść.

Pięknie.

Lekko dramatycznie.

Ale pięknie.

Dodaj Status Dostępności

Teraz pokażmy, czy produkt jest dostępny.

Zmień tę linię:

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

na tę:

const status = product.available ? "Dostępny" : "Niedostępny";
listItem.textContent = `${product.name} - €${product.price} - ${status}`;

Pełna pętla:

for (const product of products) {
  const listItem = document.createElement("li");
  const status = product.available ? "Dostępny" : "Niedostępny";

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

To używa operatora trójargumentowego.

Oznacza:

Jeśli product.available jest true, użyj "Dostępny".
W przeciwnym razie użyj "Niedostępny".

Krótkie.

Przydatne.

Na początku trochę podejrzane.

Ale po praktyce przyjazne.

Dodaj Stan Ładowania

Pobieranie danych zajmuje czas.

Dlatego powinniśmy pokazać komunikat ładowania.

Zaktualizuj loadProducts:

async function loadProducts() {
  messageElement.textContent = "Ładowanie produktów...";
  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 ? "Dostępny" : "Niedostępny";

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

  messageElement.textContent = `Wczytano ${products.length} produkty/produktów.`;
}

Teraz po kliknięciu strona mówi:

Ładowanie produktów...

To dobra jakość użytkownika.

Użytkownicy lubią wiedzieć, że coś się dzieje.

Inaczej klikają znowu.

I znowu.

I znowu.

Potem twoja aplikacja dostaje 47 żądań i zaczyna płakać.

Obsługa Błędów przez try i catch

Czasami fetch się nie uda.

Może nazwa pliku jest zła.

Może serwer nie działa.

Może internet ma filozoficzny kryzys.

Musimy obsłużyć błędy.

Zaktualizuj script.js:

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

async function loadProducts() {
  messageElement.textContent = "Ładowanie produktów...";
  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 ? "Dostępny" : "Niedostępny";

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

    messageElement.textContent = `Wczytano ${products.length} produkty/produktów.`;
  } catch (error) {
    messageElement.textContent = "Nie udało się wczytać produktów.";
    console.log(error);
  }
}

loadButton.addEventListener("click", loadProducts);

Teraz błędy nie niszczą nastroju po cichu.

Jeśli coś pójdzie nie tak, użytkownik zobaczy komunikat.

A developer zobaczy błąd w konsoli.

Wszyscy dostają informację.

Bardzo zdrowo.

Bardzo rzadko.

Sprawdzanie response.ok

Jest jeszcze jeden ważny szczegół.

fetch() nie zawsze rzuca błąd przy złych odpowiedziach HTTP.

Na przykład, jeśli plik nie istnieje, serwer może zwrócić 404.

Dlatego powinniśmy sprawdzić:

if (!response.ok) {
  throw new Error("Nie udało się wczytać produktów.");
}

Zaktualizuj część z fetch:

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

if (!response.ok) {
  throw new Error("Nie udało się wczytać produktów.");
}

const products = await response.json();

Pełna funkcja:

async function loadProducts() {
  messageElement.textContent = "Ładowanie produktów...";
  productListElement.innerHTML = "";

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

    if (!response.ok) {
      throw new Error("Nie udało się wczytać produktów.");
    }

    const products = await response.json();

    for (const product of products) {
      const listItem = document.createElement("li");
      const status = product.available ? "Dostępny" : "Niedostępny";

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

    messageElement.textContent = `Wczytano ${products.length} produkty/produktów.`;
  } catch (error) {
    messageElement.textContent = "Nie udało się wczytać produktów.";
    console.log(error);
  }
}

Teraz kod jest mocniejszy.

Nie nieśmiertelny.

Ale mocniejszy.

Jak kawa po drugiej filiżance.

Dodaj Lepsze Style

Sprawmy, żeby strona wyglądała lepiej.

Zaktualizuj index.html:

<!DOCTYPE html>
<html lang="pl">
<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">Wczytaj Produkty</button>

    <p id="message" class="message">Kliknij przycisk, aby wczytać produkty.</p>

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

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

Teraz zaktualizuj wyświetlanie produktów w script.js:

for (const product of products) {
  const listItem = document.createElement("li");
  const status = product.available ? "Dostępny" : "Niedostępny";
  const statusClass = product.available ? "available" : "unavailable";

  listItem.className = "product";

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

  productListElement.appendChild(listItem);
}

Teraz produkty wyglądają jak karty.

Dużo lepiej.

Mniej “surowy HTML z jaskini.”

Bardziej “mała aplikacja frontendowa dla początkujących.”

Postęp.

Pełny Kod JavaScript

Oto pełny script.js:

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

async function loadProducts() {
  messageElement.textContent = "Ładowanie produktów...";
  productListElement.innerHTML = "";

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

    if (!response.ok) {
      throw new Error("Nie udało się wczytać produktów.");
    }

    const products = await response.json();

    for (const product of products) {
      const listItem = document.createElement("li");
      const status = product.available ? "Dostępny" : "Niedostępny";
      const statusClass = product.available ? "available" : "unavailable";

      listItem.className = "product";

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

      productListElement.appendChild(listItem);
    }

    messageElement.textContent = `Wczytano ${products.length} produkty/produktów.`;
  } catch (error) {
    messageElement.textContent = "Nie udało się wczytać produktów.";
    console.log(error);
  }
}

loadButton.addEventListener("click", loadProducts);

Teraz masz:

To już poważny projekt dla początkujących.

Mały.

Ale poważny.

Jak małe espresso.

Typowe Błędy

Otwieranie index.html Bezpośrednio

Źle:

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

Lepiej:

caddy file-server --listen :8080

Potem otwórz:

http://localhost:8080

Fetch często potrzebuje serwera.

Nawet małego lokalnego.

Nie walcz z przeglądarką.

Ona ma zasady.

Dużo zasad.

Zapomnienie await

Źle:

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

Poprawnie:

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

Bez await nie masz jeszcze danych.

Masz promise.

Promise to nie wynik.

To przyszły wynik.

Jak zamówienie pizzy.

Paragon to nie pizza.

Znowu pizza.

Ale działa.

Zapomnienie response.json()

Źle:

const products = await response;

Poprawnie:

const products = await response.json();

Response to nie końcowe dane.

Musisz zamienić go na JSON.

JavaScript jest potężny.

Ale nadal potrzebuje instrukcji.

Zła Ścieżka do Pliku

Jeśli plik nazywa się:

products.json

to fetch:

fetch("products.json")

Jeśli plik jest w folderze:

data/products.json

to fetch:

fetch("data/products.json")

Ścieżki muszą się zgadzać.

JavaScript nie będzie szukał po folderach jak detektyw.

Po prostu się wywali.

Cicho.

Jak kot zrzucający coś ze stołu.

Praktyka

Utwórz nowy plik JSON o nazwie students.json.

Dodaj to:

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

Utwórz przycisk, który wczytuje studentów.

Pokaż każdego studenta na stronie.

Przykładowy wynik:

Anna - Beginner
Marco - Intermediate
Viktor - Beginner

Bonus:

Pokaż, ilu studentów zostało wczytanych.

Bardzo podobne do produktów.

Inne dane.

Ten sam schemat.

Tak działa programowanie.

Naucz się jednego schematu.

Używaj go wszędzie.

Udawaj, że było trudno.

Bardzo profesjonalnie.

Mini Wyzwanie

Zbuduj mały katalog kursów.

Utwórz courses.json z:

Potem użyj fetch(), żeby wczytać kursy i pokazać je jako karty.

Każda karta powinna pokazywać:

Bonus:

Pokaż tylko dostępne kursy.

To znaczy, że możesz użyć:

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

To wyzwanie łączy:

Krótko mówiąc, JavaScript zaczyna być małą fabryką aplikacji webowych.

Gratulacje.

I jeszcze jedno: ostrożnie.

Fabryki potrzebują organizacji.

Podsumowanie

Dzisiaj nauczyłeś się:

To ogromny krok.

Twój JavaScript może teraz rozmawiać z zewnętrznymi danymi.

Nie tylko ze zmiennymi.

Nie tylko z local storage.

Prawdziwe pliki.

API później.

Serwery później.

Świat zewnętrzny.

JavaScript otworzył okno.

Oby nie złe.

Następna Lekcja

W następnej lekcji zbudujemy mały projekt końcowy.

Połączymy:

Prawdziwy projekt JavaScript dla początkujących.

Nie ogromny.

Nie straszny.

Ale kompletny.

Final boss się zbliża.

Przynieś kawę.