Zum Inhalt springen

fetch & HTTP Grundlagen

Bisher lebten alle Daten im Arbeitsspeicher oder im localStorage. In echten Anwendungen kommen Daten von einem Server — per HTTP. Bevor wir React damit verbinden, schauen wir uns an, wie das in JavaScript und TypeScript funktioniert.

Als Datenquelle verwenden wir JSONPlaceholder — eine öffentliche Mock-API, die echte HTTP-Anfragen beantwortet, ohne dass wir ein Backend aufsetzen müssen.

JSONPlaceholder liefert Todos in diesem Format:

type Todo = {
id: number
title: string
completed: boolean
userId: number
}

Die Endpunkte, die wir verwenden:

MethodeURLBeschreibung
GET/todosAlle Todos (200 Einträge)
GET/todos/:idEin einzelnes Todo
POST/todosNeues Todo anlegen (Mock)
PATCH/todos/:idTodo aktualisieren (Mock)
DELETE/todos/:idTodo löschen (Mock)

fetch ist die native Browser-API für HTTP-Anfragen. Sie gibt ein Promise zurück:

const response = await fetch('https://jsonplaceholder.typicode.com/todos')
const todos = await response.json()
console.log(todos) // Array mit 200 Todos

fetch löst das Promise sobald der Server antwortet — unabhängig davon, ob die Antwort ein Fehler ist. Ein 404 oder 500 ist kein abgelehntes Promise. Deshalb muss response.ok explizit geprüft werden:

const response = await fetch('https://jsonplaceholder.typicode.com/todos')
if (!response.ok) {
throw new Error(`HTTP-Fehler: ${response.status}`)
}
const todos = await response.json()

response.json() gibt Promise<any> zurück — TypeScript weiß nichts über die Struktur. Du kannst den Typ per Cast angeben:

type Todo = {
id: number
title: string
completed: boolean
userId: number
}
async function fetchTodos(): Promise<Todo[]> {
const response = await fetch('https://jsonplaceholder.typicode.com/todos')
if (!response.ok) throw new Error(`HTTP-Fehler: ${response.status}`)
return response.json() as Promise<Todo[]>
}
async function fetchTodo(id: number): Promise<Todo> {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
if (!response.ok) throw new Error(`HTTP-Fehler: ${response.status}`)
return response.json() as Promise<Todo>
}

TypeScript vertraut dir beim Cast — es prüft nicht, ob die API wirklich dieses Format zurückgibt. Für produktionskritische Daten lohnt sich eine Validierungsbibliothek wie Zod.

Für schreibende Anfragen übergibt man Methode, Header und Body:

async function createTodo(title: string): Promise<Todo> {
const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, completed: false, userId: 1 }),
})
if (!response.ok) throw new Error(`HTTP-Fehler: ${response.status}`)
return response.json() as Promise<Todo>
}

JSONPlaceholder gibt das gesendete Objekt mit einer generierten id zurück — der Datensatz wird aber nicht wirklich gespeichert.

axios ist eine weit verbreitete HTTP-Bibliothek mit einem ergonomischeren API:

Terminal-Fenster
pnpm add axios

Der wichtigste Unterschied zu fetch:

  • Automatisches JSON-Parsing — kein manuelles .json() nötig
  • Fehler bei HTTP-Fehlerstatus — ein 404 oder 500 wirft direkt einen Fehler, response.ok entfällt
  • Interceptors — zentrales Logging, Auth-Token injizieren, globale Fehlerbehandlung
import axios from 'axios'
const client = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
})
async function fetchTodos(): Promise<Todo[]> {
const { data } = await client.get<Todo[]>('/todos')
return data
}
async function fetchTodo(id: number): Promise<Todo> {
const { data } = await client.get<Todo>(`/todos/${id}`)
return data
}
async function createTodo(title: string): Promise<Todo> {
const { data } = await client.post<Todo>('/todos', {
title,
completed: false,
userId: 1,
})
return data
}

client.get<Todo[]>('/todos') gibt direkt { data: Todo[] } zurück — kein Cast auf response.json(), der Typ kommt vom Generic.

fetchaxios
Eingebaut im Browserjanein (npm-Paket)
JSON-Parsingmanuellautomatisch
HTTP-Fehler als Exceptionneinja
Interceptorsneinja
Paketgröße0 kb~14 kb

Für einfache Requests reicht fetch. Sobald du Auth-Token, globale Fehlerbehandlung oder Interceptors brauchst, spart axios viel Boilerplate.

Diese Funktionen laufen gut in Node oder der Browser-Konsole. Aber wie binden wir sie in eine React-Komponente ein? Wir können fetchTodos() nicht direkt im Komponentenrumpf aufrufen — das würde bei jedem Render eine neue Anfrage starten und eine Endlosschleife auslösen.

React braucht einen Mechanismus, um Seiteneffekte — also alles, was über das reine Rendering hinausgeht — kontrolliert auszuführen. Das ist useEffect, das nächste Kapitel.