JavaScript Fetch API & AJAX
Die Fetch API ist die moderne Methode, um HTTP-Requests zu senden und mit APIs zu kommunizieren. Sie ersetzt das alte XMLHttpRequest (AJAX).
Was ist Fetch?
// GET Request - Daten abrufen
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error('Fehler:', error);
});
// Oder mit async/await (moderner!)
async function fetchUsers() {
try {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fehler:', error);
}
}
fetchUsers();GET Requests - Daten abrufen
Der Standard-Request ist ein GET Request.
// Einfacher GET Request
async function getUsers() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
// Response Status prüfen
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
const users = await response.json();
console.log('Users:', users);
return users;
} catch (error) {
console.error('Fehler beim Laden:', error);
throw error;
}
}
getUsers();Response Objekt verstehen
async function analyzeResponse() {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
console.log('Status:', response.status); // 200
console.log('Status Text:', response.statusText); // 'OK'
console.log('OK?:', response.ok); // true (bei 200-299)
console.log('Headers:', response.headers);
console.log('URL:', response.url);
// Response Body parsen
const data = await response.json(); // Als JSON
// const text = await response.text(); // Als Text
// const blob = await response.blob(); // Als Blob (Bilder, etc.)
console.log('Data:', data);
}
analyzeResponse();POST Requests - Daten senden
POST Requests senden Daten zum Server.
async function createUser(userData) {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
const newUser = await response.json();
console.log('User erstellt:', newUser);
return newUser;
} catch (error) {
console.error('Fehler:', error);
throw error;
}
}
// Verwenden
createUser({
name: 'Anna Schmidt',
email: 'anna@example.com',
age: 25
});PUT & PATCH Requests - Daten aktualisieren
PUT = Vollständiges Update, PATCH = Teilweises Update
// PUT - Vollständiges Update
async function updateUser(userId, userData) {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
return await response.json();
}
// PATCH - Teilweises Update
async function patchUser(userId, updates) {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updates)
});
return await response.json();
}
// Verwenden
updateUser(1, {
name: 'Anna Schmidt',
email: 'anna@example.com',
age: 26
});
patchUser(1, {
age: 26 // Nur age updaten
});DELETE Requests - Daten löschen
async function deleteUser(userId) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
console.log('User gelöscht:', userId);
return true;
} catch (error) {
console.error('Fehler beim Löschen:', error);
return false;
}
}
deleteUser(1);Headers - HTTP Header setzen
Headers enthalten Meta-Informationen über den Request.
async function fetchWithAuth() {
const response = await fetch('https://api.example.com/protected', {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE',
'Accept': 'application/json',
'X-Custom-Header': 'CustomValue'
}
});
return await response.json();
}
// Oder mit Headers Object
async function fetchWithHeadersObject() {
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', 'Bearer TOKEN');
const response = await fetch('https://api.example.com/data', {
headers: headers
});
return await response.json();
}Query Parameters - URL Parameter
// ❌ Manuell (fehleranfällig)
const url = 'https://api.example.com/search?q=javascript&limit=10';
// ✅ Mit URLSearchParams (sicherer!)
const params = new URLSearchParams({
q: 'javascript',
limit: 10,
sort: 'date'
});
const url = `https://api.example.com/search?${params.toString()}`;
console.log(url);
// 'https://api.example.com/search?q=javascript&limit=10&sort=date'
// In Fetch verwenden
async function search(query, limit = 10) {
const params = new URLSearchParams({ q: query, limit });
const response = await fetch(`https://api.example.com/search?${params}`);
return await response.json();
}
search('javascript', 20);Error Handling - Fehlerbehandlung
Wichtig: fetch() wirft nur bei Netzwerkfehlern einen Error, nicht bei HTTP-Errors (404, 500, etc.)!
async function fetchWithErrorHandling(url) {
try {
const response = await fetch(url);
// HTTP Errors manuell prüfen!
if (!response.ok) {
// 404, 500, etc.
throw new Error(`HTTP Error ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return { success: true, data };
} catch (error) {
// Netzwerkfehler oder JSON Parse Error
console.error('Fehler:', error);
return { success: false, error: error.message };
}
}
// Verwenden
const result = await fetchWithErrorHandling('https://api.example.com/data');
if (result.success) {
console.log('Daten:', result.data);
} else {
console.error('Fehler:', result.error);
}Verschiedene Error Types
async function fetchWithDetailedErrors(url) {
try {
const response = await fetch(url);
if (!response.ok) {
// Unterschiedliche HTTP Errors behandeln
switch (response.status) {
case 400:
throw new Error('Bad Request - Ungültige Anfrage');
case 401:
throw new Error('Unauthorized - Nicht autorisiert');
case 403:
throw new Error('Forbidden - Zugriff verweigert');
case 404:
throw new Error('Not Found - Ressource nicht gefunden');
case 500:
throw new Error('Server Error - Interner Serverfehler');
default:
throw new Error(`HTTP Error: ${response.status}`);
}
}
return await response.json();
} catch (error) {
if (error instanceof TypeError) {
// Netzwerkfehler
console.error('Netzwerkfehler:', error);
} else {
// HTTP oder andere Fehler
console.error('Fehler:', error.message);
}
throw error;
}
}Timeout - Request abbrechen
AbortController erlaubt es, Requests abzubrechen.
async function fetchWithTimeout(url, timeout = 5000) {
// AbortController erstellen
const controller = new AbortController();
const signal = controller.signal;
// Timeout Timer
const timeoutId = setTimeout(() => {
controller.abort(); // Request abbrechen
}, timeout);
try {
const response = await fetch(url, { signal });
clearTimeout(timeoutId); // Timer stoppen
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.error('Request Timeout!');
throw new Error('Request dauerte zu lange');
}
throw error;
}
}
// Verwenden
try {
const data = await fetchWithTimeout('https://api.example.com/slow', 3000);
console.log(data);
} catch (error) {
console.error('Fehler:', error.message);
}Praktische Beispiele
Beispiel 1: User Liste laden und anzeigen
async function loadAndDisplayUsers() {
const container = document.getElementById('users');
// Loading anzeigen
container.innerHTML = '<p>Laden...</p>';
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error('Fehler beim Laden');
}
const users = await response.json();
// HTML generieren
const html = users.map(user => `
<div class="user-card">
<h3>${user.name}</h3>
<p>Email: ${user.email}</p>
<p>Stadt: ${user.address.city}</p>
</div>
`).join('');
container.innerHTML = html;
} catch (error) {
container.innerHTML = `<p class="error">Fehler: ${error.message}</p>`;
}
}
loadAndDisplayUsers();Beispiel 2: Suchfunktion mit Debouncing
let searchTimeout;
async function searchUsers(query) {
// Debouncing - warte 300ms bevor Request
clearTimeout(searchTimeout);
searchTimeout = setTimeout(async () => {
if (!query) {
displayResults([]);
return;
}
try {
const response = await fetch(`https://api.example.com/search?q=${query}`);
const results = await response.json();
displayResults(results);
} catch (error) {
console.error('Suchfehler:', error);
}
}, 300);
}
function displayResults(results) {
const container = document.getElementById('results');
if (results.length === 0) {
container.innerHTML = '<p>Keine Ergebnisse</p>';
return;
}
container.innerHTML = results.map(item => `
<div class="result">${item.name}</div>
`).join('');
}
// Event Listener
document.getElementById('search').addEventListener('input', (e) => {
searchUsers(e.target.value);
});Beispiel 3: Form Submission
async function handleFormSubmit(event) {
event.preventDefault();
const form = event.target;
const submitBtn = form.querySelector('button[type="submit"]');
const statusDiv = document.getElementById('status');
// Form Data sammeln
const formData = {
name: form.name.value,
email: form.email.value,
message: form.message.value
};
// Button deaktivieren
submitBtn.disabled = true;
submitBtn.textContent = 'Senden...';
statusDiv.textContent = '';
try {
const response = await fetch('https://api.example.com/contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
});
if (!response.ok) {
throw new Error('Fehler beim Senden');
}
const result = await response.json();
// Erfolg!
statusDiv.textContent = 'Nachricht gesendet!';
statusDiv.className = 'success';
form.reset();
} catch (error) {
// Fehler
statusDiv.textContent = `Fehler: ${error.message}`;
statusDiv.className = 'error';
} finally {
// Button wieder aktivieren
submitBtn.disabled = false;
submitBtn.textContent = 'Senden';
}
}
// Event Listener
document.getElementById('contact-form').addEventListener('submit', handleFormSubmit);Beispiel 4: File Upload
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('title', file.name);
try {
const response = await fetch('https://api.example.com/upload', {
method: 'POST',
body: formData // KEIN Content-Type Header! Browser setzt automatisch
});
if (!response.ok) {
throw new Error('Upload fehlgeschlagen');
}
const result = await response.json();
console.log('Upload erfolgreich:', result);
return result;
} catch (error) {
console.error('Upload Fehler:', error);
throw error;
}
}
// Event Listener für File Input
document.getElementById('file-input').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
try {
const result = await uploadFile(file);
alert(`Datei hochgeladen: ${result.url}`);
} catch (error) {
alert('Upload fehlgeschlagen!');
}
});Request Options - Alle Optionen
const options = {
method: 'POST', // GET, POST, PUT, PATCH, DELETE
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer TOKEN'
},
body: JSON.stringify({ // Nur bei POST/PUT/PATCH
name: 'Anna'
}),
mode: 'cors', // cors, no-cors, same-origin
credentials: 'include', // include, same-origin, omit
// include = Cookies mitsenden
cache: 'default', // default, no-store, reload, no-cache, force-cache
redirect: 'follow', // follow, error, manual
referrer: 'client', // no-referrer, client
signal: controller.signal // AbortController Signal
};
const response = await fetch('https://api.example.com/data', options);Fetch vs. Axios
Fetch ist eingebaut, Axios ist eine externe Library.
// Fetch (eingebaut)
async function fetchExample() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error(error);
}
}
// Axios (externe Library)
async function axiosExample() {
try {
const response = await axios.get('https://api.example.com/users');
// response.data ist automatisch JSON!
// HTTP Errors werfen automatisch Errors!
return response.data;
} catch (error) {
console.error(error);
}
}
// Vorteile Fetch:
// ✅ Eingebaut, keine Library nötig
// ✅ Modern und standardisiert
// Vorteile Axios:
// ✅ Automatisches JSON Parsing
// ✅ HTTP Errors werfen automatisch Errors
// ✅ Request/Response Interceptors
// ✅ Bessere Error Messages📝 Quiz
Wann wirft fetch() einen Error?
Tipps & Tricks
Retry Logic implementieren
async function fetchWithRetry(url, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url);
if (response.ok) {
return await response.json();
}
// Nur bei Server Errors (500+) wiederholen
if (response.status < 500) {
throw new Error(\`HTTP Error: \${response.status}\`);
}
} catch (error) {
if (i === maxRetries - 1) {
throw error; // Letzter Versuch fehlgeschlagen
}
// Warte vor erneutem Versuch (exponential backoff)
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
Request Caching
const cache = new Map();
async function fetchWithCache(url, ttl = 60000) {
// Cache prüfen
const cached = cache.get(url);
if (cached && Date.now() - cached.time < ttl) {
return cached.data;
}
// Fetch neu
const response = await fetch(url);
const data = await response.json();
// In Cache speichern
cache.set(url, { data, time: Date.now() });
return data;
}
Parallel Requests
// ❌ Sequentiell (langsam)
const users = await fetch('/api/users').then(r => r.json());
const posts = await fetch('/api/posts').then(r => r.json());
// ✅ Parallel (schnell!)
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);
Progress Tracking (File Upload)
async function uploadWithProgress(file) {
const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => {
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(\`Upload: \${percent.toFixed(0)}%\`);
}
});
xhr.addEventListener('load', () => {
resolve(JSON.parse(xhr.response));
});
xhr.addEventListener('error', () => {
reject(new Error('Upload failed'));
});
const formData = new FormData();
formData.append('file', file);
xhr.open('POST', '/api/upload');
xhr.send(formData);
});
}
Häufige Fehler
Fehler 1: response.ok nicht prüfen
❌ FALSCH:
const response = await fetch('https://api.example.com/data');
const data = await response.json(); // Fehler bei 404!
✅ RICHTIG:
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(\`HTTP Error: \${response.status}\`);
}
const data = await response.json();
Fehler 2: Body doppelt lesen
❌ FALSCH:
const response = await fetch('https://api.example.com/data');
const data1 = await response.json();
const data2 = await response.json(); // Error: Already read!
✅ RICHTIG:
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Verwende 'data' mehrfach
Fehler 3: JSON.stringify vergessen
❌ FALSCH:
fetch('https://api.example.com/users', {
method: 'POST',
body: { name: 'Anna' } // ❌ Objekt!
});
✅ RICHTIG:
fetch('https://api.example.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Anna' }) // ✅ String!
});
Fehler 4: Content-Type bei FormData
❌ FALSCH:
const formData = new FormData();
formData.append('file', file);
fetch('/upload', {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data' // ❌ Falsch!
},
body: formData
});
✅ RICHTIG:
const formData = new FormData();
formData.append('file', file);
fetch('/upload', {
method: 'POST',
// KEIN Content-Type Header! Browser setzt automatisch
body: formData
});
Zusammenfassung
Du hast gelernt:
- ✅
fetch()für HTTP Requests (GET, POST, PUT, DELETE) - ✅ Async/Await für sauberen Code
- ✅ Response Object und
.json()Parsing - ✅ Error Handling mit
response.okCheck - ✅ Headers und Authorization
- ✅ Query Parameters mit URLSearchParams
- ✅ AbortController für Timeouts
- ✅ FormData für File Uploads
- ✅ Praktische Patterns (Retry, Caching, Debouncing)
Key Takeaways:
fetch()wirft nur bei Netzwerkfehlern Errorsresponse.okmanuell prüfen für HTTP Errors!response.json()nur einmal aufrufenJSON.stringify()für Request Body- AbortController für Timeouts/Abbrechen
- FormData ohne Content-Type Header
Best Practices:
- Immer Error Handling implementieren
- Async/Await statt Promise Chains
- Timeout mit AbortController
- Loading States im UI anzeigen
- Retry Logic für kritische Requests
- Request Caching wo sinnvoll
Praktische Übungen
Übung 1: GET Request
Lade User-Daten von https://jsonplaceholder.typicode.com/users und zeige Namen in einer Liste an.
Übung 2: POST Request
Erstelle einen neuen Post mit:
{
title: 'Mein Titel',
body: 'Mein Inhalt',
userId: 1
}
URL: https://jsonplaceholder.typicode.com/posts
Übung 3: Error Handling
Schreibe eine Funktion, die:
- Daten von einer URL lädt
- Bei Fehler 3x wiederholt
- Error Message zurückgibt
Übung 4: Search mit Debouncing
Erstelle eine Suchfunktion die:
- 300ms wartet bevor Request
- Loading State anzeigt
- Ergebnisse anzeigt
Übung 5: Real-World API
Baue eine kleine App mit:
- User Liste laden
- User Details anzeigen (onClick)
- User löschen (DELETE)
- Loading & Error States
API: https://jsonplaceholder.typicode.com/users
Gut gemacht! 🎉
Du hast "JavaScript Fetch API & AJAX - HTTP Requests einfach gemacht" abgeschlossen
Artikel bewerten
Bitte einloggen um zu bewerten
Das könnte dich auch interessieren
JavaScript Array Methods - map, filter, reduce & mehr
Meistere die wichtigsten JavaScript Array Methods. Von map über filter bis reduce - alles verständlich erklärt mit praktischen Beispielen.
JavaScript Async/Await & Promises - Asynchrone Programmierung
Lerne asynchrone Programmierung in JavaScript. Von Callbacks über Promises bis zu Async/Await - alles verständlich erklärt mit praktischen Beispielen.
JavaScript ES6+ Features - Moderne JavaScript Syntax
Lerne die wichtigsten ES6+ Features: Destructuring, Spread/Rest, Arrow Functions, Template Literals, Optional Chaining und mehr.