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:
- API;
- serwerów;
- plików JSON;
- baz danych przez endpointy backendowe;
- innych usług.
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ę:
- czym jest Fetch API;
- czym jest JSON;
- dlaczego
fetch()jest asynchroniczny; - jak załadować lokalny plik JSON;
- jak używać
asynciawait; - jak zamienić response na JSON;
- jak pokazywać komunikaty ładowania;
- jak obsługiwać błędy przez
tryicatch; - jak wyświetlać pobrane dane w DOM;
- jak zbudować małą listę produktów.
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:
id;name;price;available.
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:
- przycisk;
- miejsce na komunikat;
- listę na produkty.
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:
- załadował
products.json; - zamienił go na dane JavaScript;
- przeszedł przez produkty pętlą;
- stworzył elementy
<li>; - dodał je do strony.
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:
- plik JSON;
- stronę;
- żądanie fetch;
- stan ładowania;
- obsługę błędów;
- renderowanie w DOM.
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:
- tytułem kursu;
- kategorią;
- ceną;
- statusem dostępności.
Potem użyj fetch(), żeby wczytać kursy i pokazać je jako karty.
Każda karta powinna pokazywać:
- tytuł;
- kategorię;
- cenę;
- dostępność.
Bonus:
Pokaż tylko dostępne kursy.
To znaczy, że możesz użyć:
if (course.available) {
// show course
}
To wyzwanie łączy:
- tablice;
- obiekty;
- DOM;
- warunki;
- fetch;
- JSON.
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ę:
fetch()ładuje dane z plików lub API;- fetch jest asynchroniczny;
asyncpozwala funkcji używaćawait;awaitczeka na wyniki asynchroniczne;- JSON to popularny format danych;
response.json()zamienia response na dane JavaScript;- komunikaty ładowania poprawiają doświadczenie użytkownika;
tryicatchobsługują błędy;response.okpomaga wykrywać nieudane żądania;- pobrane dane można wyświetlać w DOM;
- lokalne serwery pomagają uniknąć problemów z lokalnymi plikami.
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:
- zmienne;
- tablice;
- obiekty;
- funkcje;
- DOM;
- zdarzenia;
- formularze;
- walidację;
- local storage;
- fetch.
Prawdziwy projekt JavaScript dla początkujących.
Nie ogromny.
Nie straszny.
Ale kompletny.
Final boss się zbliża.
Przynieś kawę.