07.10 Fetch API
Výuka
Proč Fetch API?
Webové aplikace potřebují komunikovat se serverem - načítat data, odesílat formuláře, aktualizovat obsah bez nutnosti obnovit celou stránku. Dříve se k tomu používal XMLHttpRequest (XHR), který byl složitý a nepřehledný.
Fetch API je moderní, Promise-based náhrada za XMLHttpRequest. Je jednodušší, čitelnější a skvěle se kombinuje s async/await.
Představ si to jako: Kurýra, kterého pošleš na poštu. Řekneš mu adresu (URL), co má udělat (GET/POST), a on ti přinese odpověď. Nemusíš čekat na místě - dáš mu svůj telefon (callback/Promise) a on ti zavolá, až se vrátí.
Jak to funguje?
1. Zavoláš fetch(url, options) - odešle HTTP request
2. Dostaneš Promise<Response> - slib, že přijde odpověď
3. Response obsahuje: status, headers, body (stream)
4. Body musíš "přečíst" pomocí .json(), .text(), .blob() atd.
5. Toto čtení je TAKÉ asynchronní - vrací další Promise
Struktura HTTP požadavku
┌─────────────────────────────────────────┐
│ HTTP Request │
├─────────────────────────────────────────┤
│ Method: GET / POST / PUT / DELETE │
│ URL: https://api.example.com/users │
│ Headers: Content-Type, Authorization... │
│ Body: (jen u POST/PUT) JSON, FormData...│
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ HTTP Response │
├─────────────────────────────────────────┤
│ Status: 200, 404, 500... │
│ Headers: Content-Type, Content-Length...│
│ Body: JSON, HTML, obrázek, soubor... │
└─────────────────────────────────────────┘
Klíčové koncepty
- Request - co posíláš na server (URL, metoda, hlavičky, tělo)
- Response - co server vrací (status, hlavičky, tělo)
- Status code - číslo říkající úspěch/chybu (200 = OK, 404 = nenalezeno, 500 = chyba serveru)
- Headers - metadata požadavku/odpovědi (typ obsahu, autorizace...)
- Body - samotná data (JSON, text, binární...)
Důležité: Fetch NEVYHAZUJE chybu při 404 nebo 500!
Na rozdíl od mnoha knihoven, fetch vyhazuje chybu POUZE při síťové chybě (odpojení od internetu, CORS blokace). HTTP chyby (404, 500) jsou "úspěšné" odpovědi - musíš zkontrolovat response.ok nebo response.status!
JavaScript
// ==========================================
// 1. Základní GET request
// ==========================================
async function nactiUzivatele() {
const response = await fetch("https://api.example.com/users");
// DŮLEŽITÉ: Zkontrolovat jestli byl request úspěšný
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// Přečíst tělo odpovědi jako JSON
const users = await response.json();
return users;
}
// Použití
nactiUzivatele()
.then(users => console.log("Uživatelé:", users))
.catch(error => console.error("Chyba:", error));
// ==========================================
// 2. POST request s daty
// ==========================================
async function vytvorUzivatele(userData) {
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json", // Říkáme serveru co posíláme
"Accept": "application/json" // Říkáme co chceme dostat zpět
},
body: JSON.stringify(userData) // Tělo musí být string!
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || "Nepodařilo se vytvořit uživatele");
}
return response.json(); // Vrátí vytvořeného uživatele
}
// Použití
vytvorUzivatele({
name: "Jan Novák",
email: "jan@example.com"
})
.then(user => console.log("Vytvořen:", user))
.catch(error => console.error("Chyba:", error));
// ==========================================
// 3. Kompletní příklad s error handling
// ==========================================
async function fetchWithErrorHandling(url, options = {}) {
try {
const response = await fetch(url, options);
// Zpracování různých HTTP statusů
if (response.status === 401) {
throw new Error("Nepřihlášen - přihlas se prosím");
}
if (response.status === 403) {
throw new Error("Nemáš oprávnění");
}
if (response.status === 404) {
throw new Error("Zdroj nenalezen");
}
if (!response.ok) {
throw new Error(`Server error: ${response.status}`);
}
// Různé typy odpovědí
const contentType = response.headers.get("content-type");
if (contentType?.includes("application/json")) {
return await response.json();
}
if (contentType?.includes("text/")) {
return await response.text();
}
// Pro ostatní (obrázky, soubory...)
return await response.blob();
} catch (error) {
// Rozlišení síťové vs HTTP chyby
if (error.name === "TypeError") {
throw new Error("Síťová chyba - zkontroluj připojení");
}
throw error;
}
}
Co se stalo?
fetch()vrací Promise<Response> - čekáme na odpověď serveruresponse.okje true pro statusy 200-299, false pro ostatníresponse.json()parsuje JSON z těla - je to TAKÉ asynchronní!- U POST musíme nastavit
method,headersabody bodymusí být string - protoJSON.stringify()
Další HTTP metody
// PUT - aktualizace celého zdroje
await fetch("/api/users/123", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Nové jméno", email: "novy@email.cz" })
});
// PATCH - částečná aktualizace
await fetch("/api/users/123", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Jen změna jména" })
});
// DELETE - smazání
await fetch("/api/users/123", {
method: "DELETE"
});
TypeScript
// ==========================================
// Typované Fetch API
// ==========================================
// Definice typů pro API odpovědi
interface User {
id: number;
name: string;
email: string;
createdAt: string;
}
interface CreateUserRequest {
name: string;
email: string;
}
interface ApiError {
message: string;
code: string;
}
// Generická fetch funkce s typováním
async function fetchApi<T>(
url: string,
options?: RequestInit
): Promise<T> {
const response = await fetch(url, options);
if (!response.ok) {
const error: ApiError = await response.json();
throw new Error(error.message);
}
return response.json() as Promise<T>;
}
// Typované API funkce
async function getUsers(): Promise<User[]> {
return fetchApi<User[]>("/api/users");
}
async function getUser(id: number): Promise<User> {
return fetchApi<User>(`/api/users/${id}`);
}
async function createUser(data: CreateUserRequest): Promise<User> {
return fetchApi<User>("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
});
}
async function deleteUser(id: number): Promise<void> {
await fetch(`/api/users/${id}`, { method: "DELETE" });
}
// Použití - TypeScript ví co dostaneš
async function main(): Promise<void> {
const users = await getUsers();
// users je User[] - máme autocomplete pro .name, .email, atd.
const user = await getUser(123);
console.log(user.name); // TypeScript ví, že user má property name
const newUser = await createUser({
name: "Jan",
email: "jan@example.com"
// TypeScript by oznámil chybu, kdybychom zapomněli name nebo email
});
}
// ==========================================
// Pokročilé: Type guard pro API odpovědi
// ==========================================
interface SuccessResponse<T> {
success: true;
data: T;
}
interface ErrorResponse {
success: false;
error: string;
}
type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;
async function safeFetch<T>(url: string): Promise<ApiResponse<T>> {
try {
const response = await fetch(url);
const data = await response.json();
if (!response.ok) {
return {
success: false,
error: data.message || `HTTP ${response.status}`
};
}
return { success: true, data: data as T };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error"
};
}
}
// Použití s type narrowing
async function demo(): Promise<void> {
const result = await safeFetch<User[]>("/api/users");
if (result.success) {
// TypeScript ví: result.data je User[]
result.data.forEach(user => console.log(user.name));
} else {
// TypeScript ví: result.error je string
console.error("Chyba:", result.error);
}
}
TypeScript přidává:
- ✅ Typované Response body (
User,User[]) - ✅ Typované Request body (
CreateUserRequest) - ✅ Generické fetchApi<T> pro znovupoužitelnost
- ✅ Autocomplete při práci s odpovědí
- ✅ Chyba při zapomenutí povinného fieldu
Rozdíl JS vs TS
JavaScript:
response.json()vracíany- můžeš přistupovat k čemukoliv- Žádná kontrola, jestli data mají očekávanou strukturu
- Chyby se projeví až za běhu
TypeScript:
response.json()vracíPromise(nebo přetypuješ)- Musíš deklarovat, co očekáváš
- Chyby v typech odhalíš při kompilaci
// TypeScript rozdíl v praxi
// ❌ JavaScript - nebezpečné
const data = await response.json();
console.log(data.users[0].profile.avatar);
// Pokud struktura nesedí → runtime crash!
// ✅ TypeScript - bezpečné
interface ApiData {
users: Array<{
profile: { avatar: string }
}>
}
const data: ApiData = await response.json();
console.log(data.users[0].profile.avatar);
// TypeScript ví přesně co očekávat
// Pokud API změní strukturu → chyba při kompilaci
Tip
💡 Abort Controller - zrušení requestu:
// Užitečné pro: timeout, cancel při odchodu ze stránky, debounce
const controller = new AbortController();
// Timeout po 5 sekundách
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch("/api/slow", {
signal: controller.signal // Předáme signal do fetch
});
clearTimeout(timeoutId); // Request uspěl, zrušíme timeout
return response.json();
} catch (error) {
if (error.name === "AbortError") {
console.log("Request byl zrušen (timeout nebo manuálně)");
} else {
throw error;
}
}
💡 Query parametry správně:
// ❌ Špatně - ruční skládání může rozbít speciální znaky
const url = `/api/search?query=${searchTerm}&page=${page}`;
// ✅ Správně - URLSearchParams enkóduje automaticky
const params = new URLSearchParams({
query: searchTerm, // Automaticky enkóduje mezery, &, ? atd.
page: String(page)
});
const response = await fetch(`/api/search?${params}`);
💡 Request s autorizací:
async function authenticatedFetch(url, options = {}) {
const token = localStorage.getItem("authToken");
return fetch(url, {
...options,
headers: {
...options.headers,
"Authorization": `Bearer ${token}`
}
});
}
Kvíz
Co vrací fetch() když server odpoví statusem 404 (Not Found)?
❌ - Fetch NEVYHAZUJE chybu při HTTP chybových statusech (404, 500, atd.)! To je častý zdroj bugů.
✅ - Správně! Fetch vrací úspěšný Promise s Response objektem. Musíš zkontrolovat response.ok nebo response.status sám.
❌ - Fetch vždy vrací Promise, nikdy přímo null.
❌ - Fetch vždy vrací Promise, nikdy přímo undefined.
Klíčové poučení: Fetch vyhazuje chybu POUZE při síťových problémech (žádné připojení, CORS blokace). HTTP statusy jako 404 nebo 500 jsou považovány za "úspěšné" odpovědi serveru!
🎯 Závěrečný projekt
Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.
Zobrazit podrobnosti projektu →Informace o seriálu
Obtížnost
Délka
Cca 480 minut
Počet videí
8 videí + projekty
Certifikát
Po dokončení obdržíte certifikát
Lekce v této sekci
- 7.1 Synchronní vs asynchronní programování
- 7.2 Callbacks
- 07.03 Callback Hell
- 07.04 Promises úvod
- 07.05 Then a Catch
- 07.06 Promise.all
- 07.07 Promise.race
- 07.08 Async/Await
- 07.09 Try/Catch s async
- 07.10 Fetch API
- 7.11 Generátory
Struktura lekcí (souborový strom)
- 1.1 Úvod do JavaScriptu a TypeScriptu
- 1.2 Nastavení prostředí
- 1.3 První program
- 1.4 Proměnné: var, let, const
- 1.5 Datové typy - přehled
- 1.6 String (řetězce)
- 1.7 Number (čísla)
- 1.8 Boolean (pravda/nepravda)
- 1.9 Null a Undefined
- 1.10 Type Inference vs Annotations
- 1.11 Aritmetické operátory
- 1.12 Porovnávací operátory
- 1.13 Logické operátory
- 1.14 Komentáře
- 1.15 Console metody
- 03.01 Deklarace funkce
- 03.02 Function Expression
- 03.03 Arrow Functions
- 03.04 Parametry a argumenty
- 03.05 Return hodnoty
- 03.06 Výchozí parametry
- 03.07 Rest parametry
- 03.08 Co je Scope
- 03.09 Lexikální Scope
- 03.10 Řetězec Scope
- 03.11 Globální Scope
- 03.12 Životní Cyklus Proměnných
- 03.13 Omezení Scope
- 03.14 Použití Closures
- 03.15 Callback funkce
- 03.16 Higher-order Functions
- 03.17 IIFE
- 03.18 Void funkce
- 03.19 Rekurze
- v přípravě
- v přípravě
- v přípravě
- v přípravě
- 01 — Co je Next.js
- 02 — Vytvoření projektu
- 03 — Struktura projektu (app/)
- 04 — Page komponenty (page.js / page.tsx)
- 05 — Layout komponenty (layout.js / layout.tsx)
- 06 — File-based routing
- 07 — Dynamické routy ([id]/page.js)
- 08 — Link komponenta (navigace)
- 09 — Image komponenta (next/image)
- 10 — Metadata (title, description, Open Graph)
- 11 — Loading UI (loading.js / loading.tsx)
- 12 — Error handling (error.js / error.tsx)
- 13 — Not Found (not-found.js / not-found.tsx)
- v přípravě
- v přípravě
- v přípravě