# JavaScript APIs & Promises - Anfänger Tutorial
## 📚 Inhaltsverzeichnis
1. [Promise Grundlagen](#promise-grundlagen)
2. [Async/Await](#asyncawait)
3. [Try/Catch Fehlerbehandlung](#trycatch-fehlerbehandlung)
4. [Promise Chaining mit .then()](#promise-chaining-mit-then)
5. [Fetch API](#fetch-api)
6. [Externe APIs nutzen](#externe-apis-nutzen)
7. [**Firebase Realtime Database**](#firebase-realtime-database) ⭐ **NEU**
8. [Praxisbeispiele](#praxisbeispiele)
---
## 🎯 Promise Grundlagen
### Was ist ein Promise?
- **Versprechen** für ein zukünftiges Ergebnis
- **Drei Zustände:** pending, fulfilled, rejected
- **Asynchrone Operationen** ohne Callback-Hell
### Basis Promise Syntax
```javascript
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        if (success) {
            resolve("Erfolgreich!");
        } else {
            reject("Fehler aufgetreten!");
        }
    }, 1000);
});
```
### Einfaches Beispiel
```javascript
function getPromise() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            let success = true;
            if (success) {
                resolve("Hat geklappt!");
            } else {
                reject("Hat nicht geklappt!");
            }
        }, 1000);
    });
}
```
---
## 🔄 Async/Await
### Vorteile von Async/Await
- **Sauberer Code** - keine Callback-Hell
- **Leserlicher** als Promise-Chains
- **Einfache Fehlerbehandlung** mit try/catch
### Syntax
```javascript
async function meineFunktion() {
    try {
        let ergebnis = await getPromise();
        console.log(ergebnis);
    } catch (error) {
        console.error(error);
    }
}
```
### Mehrere Promises abwarten
```javascript
async function mehrerePromises() {
    try {
        await getPromise1();
        await getPromise2();
        await getPromise3();
        console.log("Alle Promises abgeschlossen!");
    } catch (error) {
        console.log("Ein Fehler ist aufgetreten:", error);
    }
}
```
---
## 🛡️ Try/Catch Fehlerbehandlung
### Warum Try/Catch?
- **Fehler abfangen** ohne App-Crash
- **Benutzerfreundliche** Fehlermeldungen
- **Debugging** vereinfachen
### Beispiel mit mehreren Promises
```javascript
async function usePromise() {
    try {
        await getPromise1();
        await getPromise2();
        await getPromise3();
        console.log("Alle erfolgreich!");
    } catch (error) {
        console.log("Fehler:", error);
    } finally {
        console.log("Immer ausgeführt");
    }
}
```
---
## 🔗 Promise Chaining mit .then()
### Wann .then() verwenden?
- **Sequentielle Abarbeitung** von Promises
- **Verkettung** von Operationen
- **Alternative** zu async/await
### Basis .then() Syntax
```javascript
promise
    .then(result => {
        console.log("Erfolg:", result);
        return nextPromise();
    })
    .then(result => {
        console.log("Zweites Ergebnis:", result);
    })
    .catch(error => {
        console.error("Fehler:", error);
    });
```
### Praxisbeispiel
```javascript
function usePromise() {
    getGoodPromise()
        .then(result => {
            console.log(result);
            return getBadPromise();
        })
        .then(result => {
            console.log(result);
        })
        .catch(error => {
            console.error(error);
        });
}
```
---
## 🌐 Fetch API
### Was ist Fetch?
- **Moderne Alternative** zu XMLHttpRequest
- **Promise-basiert** für HTTP-Requests
- **Einfacher** als traditionelle AJAX-Calls
### Basis Fetch Syntax
```javascript
fetch(url)
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error));
```
### Lokale JSON-Datei laden
```javascript
async function fetchDataJson() {
    try {
        let response = await fetch('db.json');
        let data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Fehler beim Laden:', error);
    }
}
```
### Text-Datei laden
```javascript
async function fetchDataText() {
    try {
        let response = await fetch('h1.txt');
        let text = await response.text(); // Wichtig: .text() statt .json()
        document.getElementById("content").innerHTML = text;
    } catch (error) {
        console.error('Fehler:', error);
    }
}
```
---
## 🍎 Externe APIs nutzen
### Fruityvice API
- **Kostenlose API** für Fruchtdaten
- **Keine Registrierung** erforderlich
- **Basis-URL:** `https://www.fruityvice.com/api/fruit/`
### API Endpunkte
- `GET /api/fruit/all` - Alle Früchte
- `GET /api/fruit/{name}` - Spezifische Frucht
- `GET /api/fruit/random` - Zufällige Frucht
- `PUT /api/fruit` - Neue Frucht hinzufügen
### Einzelne Frucht abrufen
```javascript
async function getFruit(fruitName) {
    try {
        let response = await fetch(`https://www.fruityvice.com/api/fruit/${fruitName}`);
        let fruitData = await response.json();
        console.log(fruitData);
        return fruitData;
    } catch (error) {
        console.error('Frucht nicht gefunden:', error);
    }
}
// Verwendung
getFruit('strawberry');
```
### Alle Früchte abrufen
```javascript
async function getAllFruits() {
    try {
        let response = await fetch('https://www.fruityvice.com/api/fruit/all');
        let fruits = await response.json();
        console.log('Anzahl Früchte:', fruits.length);
        return fruits;
    } catch (error) {
        console.error('Fehler beim Laden aller Früchte:', error);
    }
}
```
### Zufällige Frucht
```javascript
async function getRandomFruit() {
    try {
        let response = await fetch('https://www.fruityvice.com/api/fruit/random');
        let randomFruit = await response.json();
        console.log('Zufällige Frucht:', randomFruit.name);
        return randomFruit;
    } catch (error) {
        console.error('Fehler bei zufälliger Frucht:', error);
    }
}
```
---
## 🔥 Firebase Realtime Database
### Was ist Firebase Realtime Database?
- **NoSQL Cloud-Datenbank** von Google
- **Realtime-Synchronisation** zwischen Clients
- **REST API** für einfache Integration
- **Kostenlos** für kleine Projekte
### Setup und Konfiguration
1. **Firebase Projekt** erstellen: [console.firebase.google.com](https://console.firebase.google.com)
2. **Realtime Database** aktivieren
3. **Sicherheitsregeln** anpassen für Development:
```json
{
  "rules": {
    ".read": true,
    ".write": true
  }
}
```
### Firebase URL Struktur
```javascript
const BASE_URL = "https://PROJEKT-NAME-default-rtdb.REGION.firebasedatabase.app/";
// Beispiel:
const BASE_URL = "https://da-porjectapi-training-default-rtdb.europe-west1.firebasedatabase.app/";
```
### 📖 READ - Daten laden (GET)
```javascript
async function loadData(){
    try {
        let response = await fetch(BASE_URL + ".json");
        
        if (!response.ok) {
            throw new Error(`HTTP Error: ${response.status}`);
        }
        
        let responseToJson = await response.json();
        console.log("Firebase data:", responseToJson);
        return responseToJson;
    } catch (error) {
        console.error("error:", error);
    }
}
// Spezifischen Pfad laden
async function loadDataFromPath(path) {
    try {
        let response = await fetch(BASE_URL + path + ".json");
        let data = await response.json();
        return data;
    } catch (error) {
        console.error("Fehler beim Laden:", error);
    }
}
// Verwendung
loadDataFromPath("users/user1"); // Lädt /users/user1
```
### ✍️ CREATE - Neue Daten hinzufügen (POST)
```javascript
async function postData(path = "", data = {}) {
    try {  
        let response = await fetch(BASE_URL + path + ".json", {
            method: "POST",
            headers: {  
                "Content-Type": "application/json",
            }, 
            body: JSON.stringify(data)
        });
        
        if (!response.ok) {
            throw new Error(`HTTP Error: ${response.status}`);
        }
        
        let responseToJson = await response.json();
        console.log("Posted data response:", responseToJson);
        return responseToJson;
        
    } catch (error) {
        console.error("Error posting data:", error);
    }
}
// Verwendung
postData("users", {
    name: "Max Mustermann",
    email: "max@example.com",
    age: 25
});
```
### 🔄 UPDATE - Daten überschreiben (PUT)
```javascript
async function putData(path = "", data = {}) {
    try {
        let response = await fetch(BASE_URL + path + ".json", {
            method: "PUT",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(data)
        });
        
        if (!response.ok) {
            throw new Error(`HTTP Error: ${response.status}`);
        }
        
        let responseToJson = await response.json();
        console.log("Data updated:", responseToJson);
        return responseToJson;
        
    } catch (error) {
        console.error("Error updating data:", error);
    }
}
// Verwendung
putData("users/user1", {
    name: "Max Mustermann Updated",
    email: "max.new@example.com"
});
```
### 🗑️ DELETE - Daten löschen (DELETE)
```javascript
async function deleteData(path = "") {
    try {
        let response = await fetch(BASE_URL + path + ".json", { 
            method: "DELETE" 
        });  
        
        if (!response.ok) {
            throw new Error(`HTTP Error: ${response.status}`);
        }
        
        console.log(`Data at path "${path}" deleted successfully`);
        return true; 
        
    } catch (error) {
        console.error("Error deleting data:", error);
        return false;
    }
}
// Verwendung
deleteData("users/user1"); // Löscht /users/user1
```
### 🔧 PATCH - Teilweise Updates
```javascript
async function patchData(path = "", data = {}) {
    try {
        let response = await fetch(BASE_URL + path + ".json", {
            method: "PATCH",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(data)
        });
        
        if (!response.ok) {
            throw new Error(`HTTP Error: ${response.status}`);
        }
        
        let responseToJson = await response.json();
        console.log("Data patched:", responseToJson);
        return responseToJson;
        
    } catch (error) {
        console.error("Error patching data:", error);
    }
}
// Verwendung - nur bestimmte Felder ändern
patchData("users/user1", {
    age: 26  // Nur age wird geändert, rest bleibt
});
```
### 📝 Vollständiges Firebase Beispiel
#### index.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Firebase Storage</title>
    <script src="remoteStorage.js"></script>
</head>
<body onload="onloadFunc()">
    <div id="content"></div>
</body>
</html>
```
#### remoteStorage.js
```javascript
const BASE_URL = "https://da-porjectapi-training-default-rtdb.europe-west1.firebasedatabase.app/";
async function loadData(){
    try {
        let response = await fetch(BASE_URL + ".json");
        
        if (!response.ok) {
            throw new Error(`HTTP Error: ${response.status}`);
        }
        
        let responseToJson = await response.json();
        console.log("Firebase data:", responseToJson);
        return responseToJson;
    } catch (error) {
        console.error("error:", error);
    }
}
async function postData(path = "", data = {}) {
    try {  
        let response = await fetch(BASE_URL + path + ".json", {
            method: "POST",
            headers: {  
                "Content-Type": "application/json",
            }, 
            body: JSON.stringify(data)
        });
        
        let responseToJson = await response.json();
        console.log("Posted data response:", responseToJson);
        return responseToJson;
        
    } catch (error) {
        console.error("Error posting data:", error);
    }
}
async function deleteData(path = "") {
    try {
        let response = await fetch(BASE_URL + path + ".json", { 
            method: "DELETE" 
        });  
        
        if (!response.ok) {
            throw new Error(`HTTP Error: ${response.status}`);
        }
        
        console.log(`Data at path "${path}" deleted successfully`);
        return true; 
        
    } catch (error) {
        console.error("Error deleting data:", error);
        return false;
    }
}
function onloadFunc(){
    console.log("App started...");
    loadData();
    postData("", {"banana": "rama"});
    deleteData("/name/"); 
}
```
### Firebase URL-Struktur verstehen
```javascript
// Basis URL
https://PROJEKT-default-rtdb.REGION.firebasedatabase.app/
// Pfade hinzufügen
/users.json                    // Alle User
/users/user1.json             // Spezifischer User
/users/user1/name.json        // Nur Name von user1
/products/electronics.json    // Elektronik-Produkte
// Beispiele:
loadData("users")              // GET /users.json
postData("users", userData)    // POST /users.json  
putData("users/user1", data)   // PUT /users/user1.json
deleteData("users/user1")      // DELETE /users/user1.json
```
### Firebase vs. andere APIs
| Feature | Firebase | REST API | GraphQL |
|---------|----------|----------|---------|
| **Setup** | Einfach | Mittel | Komplex |
| **Realtime** | ✅ Ja | ❌ Nein | ✅ Ja |
| **Offline** | ✅ Ja | ❌ Nein | ❌ Nein |
| **Kosten** | Kostenlos* | Variiert | Variiert |
| **Skalierung** | Automatisch | Manuell | Manuell |
---
## 💡 Praxisbeispiele
### Beispiel 1: Frucht-Suche
```javascript
async function searchFruit() {
    let fruitName = document.getElementById('fruitInput').value;
    
    try {
        let response = await fetch(`https://www.fruityvice.com/api/fruit/${fruitName}`);
        
        if (!response.ok) {
            throw new Error('Frucht nicht gefunden');
        }
        
        let fruit = await response.json();
        displayFruit(fruit);
    } catch (error) {
        console.error('Fehler:', error);
        document.getElementById('result').innerHTML = 'Frucht nicht gefunden!';
    }
}
function displayFruit(fruit) {
    let html = `
        <h2>${fruit.name}</h2>
        <p>Familie: ${fruit.family}</p>
        <p>Kalorien: ${fruit.nutritions.calories}</p>
        <p>Zucker: ${fruit.nutritions.sugar}g</p>
    `;
    document.getElementById('result').innerHTML = html;
}
```
### Beispiel 2: Firebase Todo-App
```javascript
// Todo hinzufügen
async function addTodo(todoText) {
    let todoData = {
        text: todoText,
        completed: false,
        created: Date.now()
    };
    
    let result = await postData("todos", todoData);
    console.log("Todo hinzugefügt:", result.name);
    return result;
}
// Alle Todos laden
async function loadTodos() {
    let todos = await loadDataFromPath("todos");
    displayTodos(todos);
}
// Todo als erledigt markieren
async function completeTodo(todoId) {
    await patchData(`todos/${todoId}`, { completed: true });
    loadTodos(); // Neu laden
}
// Todo löschen
async function deleteTodo(todoId) {
    await deleteData(`todos/${todoId}`);
    loadTodos(); // Neu laden
}
function displayTodos(todos) {
    let html = '<h2>Meine Todos:</h2><ul>';
    
    for (let id in todos) {
        let todo = todos[id];
        let status = todo.completed ? '✅' : '❌';
        html += `
            <li>
                ${status} ${todo.text}
                <button onclick="completeTodo('${id}')">Erledigt</button>
                <button onclick="deleteTodo('${id}')">Löschen</button>
            </li>
        `;
    }
    
    html += '</ul>';
    document.getElementById('todoList').innerHTML = html;
}
```
### Beispiel 3: User Management mit Firebase
```javascript
// Benutzer registrieren
async function registerUser(userData) {
    try {
        let result = await postData("users", {
            name: userData.name,
            email: userData.email,
            registered: Date.now(),
            active: true
        });
        
        console.log("User registriert:", result.name);
        return result.name; // Firebase generierte ID
        
    } catch (error) {
        console.error("Registrierung fehlgeschlagen:", error);
    }
}
// Benutzer-Profil laden
async function getUserProfile(userId) {
    try {
        let profile = await loadDataFromPath(`users/${userId}`);
        return profile;
    } catch (error) {
        console.error("Profil nicht gefunden:", error);
    }
}
// Benutzer-Profil aktualisieren
async function updateUserProfile(userId, updates) {
    try {
        await patchData(`users/${userId}`, updates);
        console.log("Profil aktualisiert");
    } catch (error) {
        console.error("Update fehlgeschlagen:", error);
    }
}
// Alle aktiven Benutzer laden
async function getActiveUsers() {
    try {
        let allUsers = await loadDataFromPath("users");
        let activeUsers = {};
        
        for (let id in allUsers) {
            if (allUsers[id].active) {
                activeUsers[id] = allUsers[id];
            }
        }
        
        return activeUsers;
    } catch (error) {
        console.error("Fehler beim Laden aktiver User:", error);
    }
}
```
### Beispiel 4: Mehrere API-Calls kombinieren
```javascript
async function compareFruits(fruit1, fruit2) {
    try {
        let [data1, data2] = await Promise.all([
            fetch(`https://www.fruityvice.com/api/fruit/${fruit1}`).then(r => r.json()),
            fetch(`https://www.fruityvice.com/api/fruit/${fruit2}`).then(r => r.json())
        ]);
        
        console.log(`${fruit1}: ${data1.nutritions.calories} Kalorien`);
        console.log(`${fruit2}: ${data2.nutritions.calories} Kalorien`);
        
        let winner = data1.nutritions.calories < data2.nutritions.calories ? fruit1 : fruit2;
        console.log(`${winner} hat weniger Kalorien!`);
        
    } catch (error) {
        console.error('Fehler beim Vergleich:', error);
    }
}
// Verwendung
compareFruits('apple', 'banana');
```
---
## 🚀 Erweiterte Techniken
### Promise.all() für parallele Requests
```javascript
async function loadMultipleFruits() {
    let fruitNames = ['apple', 'banana', 'orange'];
    
    try {
        let promises = fruitNames.map(name => 
            fetch(`https://www.fruityvice.com/api/fruit/${name}`)
                .then(response => response.json())
        );
        
        let results = await Promise.all(promises);
        console.log('Alle Früchte geladen:', results);
    } catch (error) {
        console.error('Fehler bei einem Request:', error);
    }
}
```
### Timeout für Requests
```javascript
function fetchWithTimeout(url, timeout = 5000) {
    return Promise.race([
        fetch(url),
        new Promise((_, reject) => 
            setTimeout(() => reject(new Error('Timeout')), timeout)
        )
    ]);
}
async function getFruitWithTimeout(fruitName) {
    try {
        let response = await fetchWithTimeout(
            `https://www.fruityvice.com/api/fruit/${fruitName}`,
            3000
        );
        let fruit = await response.json();
        return fruit;
    } catch (error) {
        console.error('Request zu langsam oder fehlgeschlagen:', error);
    }
}
```
### Firebase Realtime Updates (Advanced)
```javascript
// Server-Sent Events für Realtime Updates
function listenToFirebaseChanges(path) {
    const eventSource = new EventSource(
        `${BASE_URL}${path}.json?sse=true`
    );
    
    eventSource.onmessage = function(event) {
        const data = JSON.parse(event.data);
        console.log('Daten geändert:', data);
        // UI aktualisieren
        updateUI(data);
    };
    
    eventSource.onerror = function(error) {
        console.error('SSE Fehler:', error);
    };
    
    return eventSource;
}
// Verwendung
const listener = listenToFirebaseChanges('todos');
// listener.close(); // Verbindung schließen
```
---
## 🔧 Debugging-Tipps
### Console.log richtig nutzen
```javascript
async function debugFetch() {
    console.log('1. Starte Request...');
    
    try {
        let response = await fetch('https://www.fruityvice.com/api/fruit/apple');
        console.log('2. Response erhalten:', response.status);
        
        let data = await response.json();
        console.log('3. Daten geparst:', data);
        
    } catch (error) {
        console.error('4. Fehler aufgetreten:', error);
    }
}
```
### Firebase Debugging
```javascript
// Firebase Response Status prüfen
async function debugFirebaseRequest() {
    try {
        let response = await fetch(BASE_URL + ".json");
        
        console.log("Status:", response.status);
        console.log("OK:", response.ok);
        console.log("Headers:", response.headers);
        
        if (response.status === 401) {
            console.error("Firebase Berechtigung fehlt!");
        }
        
        let data = await response.json();
        console.log("Data:", data);
        
    } catch (error) {
        console.error("Firebase Fehler:", error);
    }
}
```
### Häufige Fehlerquellen
- **CORS-Probleme** bei externen APIs
- **Falsche URL** oder Endpoint  
- **Firebase Sicherheitsregeln** zu restriktiv
- **Vergessenes await** bei async Funktionen
- **response.json()** statt **response.text()** bei Text-Dateien
- **Falsche HTTP-Methoden** (GET, POST, PUT, DELETE)
---
## 📊 Performance & Best Practices
### Caching implementieren
```javascript
// Einfacher Memory Cache
const cache = new Map();
async function getCachedData(url, cacheTime = 5 * 60 * 1000) { // 5 Minuten
    const now = Date.now();
    const cached = cache.get(url);
    
    if (cached && (now - cached.timestamp) < cacheTime) {
        console.log('Aus Cache geladen');
        return cached.data;
    }
    
    console.log('Neue Anfrage');
    const response = await fetch(url);
    const data = await response.json();
    
    cache.set(url, {
        data: data,
        timestamp: now
    });
    
    return data;
}
```
### Rate Limiting
```javascript
// Request Queue für Rate Limiting
class RequestQueue {
    constructor(maxConcurrent = 3, delay = 1000) {
        this.queue = [];
        this.running = 0;
        this.maxConcurrent = maxConcurrent;
        this.delay = delay;
    }
    
    async add(fetchPromise) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                fetchPromise,
                resolve,
                reject
            });
            this.process();
        });
    }
    
    async process() {
        if (this.running >= this.maxConcurrent || this.queue.length === 0) {
            return;
        }
        
        this.running++;
        const { fetchPromise, resolve, reject } = this.queue.shift();
        
        try {
            const result = await fetchPromise();
            resolve(result);
        } catch (error) {
            reject(error);
        }
        
        this.running--;
        
        setTimeout(() => {
            this.process();
        }, this.delay);
    }
}
// Verwendung
const requestQueue = new RequestQueue(2, 500); // Max 2 parallel, 500ms delay
async function queuedRequest(url) {
    return requestQueue.add(() => fetch(url).then(r => r.json()));
}
```
---
## 📝 Zusammenfassung
### Key Takeaways
- **Promises** vereinfachen asynchrone Programmierung
- **Async/Await** macht Code lesbarer als .then()
- **Try/Catch** für saubere Fehlerbehandlung
- **Fetch API** für moderne HTTP-Requests
- **Firebase** bietet einfache Cloud-Database
- **Externe APIs** erweitern App-Funktionalität
### Firebase vs. Externe APIs
| Aspekt | Firebase | Externe APIs |
|--------|----------|--------------|
| **Setup** | Einfach | Variiert |
| **Kosten** | Kostenlos (Limits) | Oft kostenpflichtig |
| **Kontrolle** | Eigene Daten | Fremd-kontrolliert |
| **Offline** | Ja | Nein |
| **Realtime** | Ja | Selten |
### Best Practices
- **Immer Fehlerbehandlung** implementieren
- **Await nicht vergessen** bei async Funktionen
- **Loading States** für bessere UX
- **API-Limits** und **Rate Limiting** beachten
- **Caching** für Performance
- **Firebase Sicherheitsregeln** für Production
### Nächste Schritte
- **Firebase Authentication** implementieren
- **Progressive Web Apps (PWA)** entwickeln
- **Service Workers** für Offline-Funktionalität
- **WebSockets** für Realtime-Features
- **GraphQL** als Alternative zu REST
---
## 🔗 Nützliche Links
### Dokumentation
- [MDN Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
- [Firebase Dokumentation](https://firebase.google.com/docs)
- [Promises MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
### APIs zum Üben
- [JSONPlaceholder](https://jsonplaceholder.typicode.com/) - Fake REST API
- [Fruityvice](https://www.fruityvice.com/) - Frucht-Daten
- [Cat Facts API](https://catfact.ninja/) - Katzen-Fakten
- [Random User API](https://randomuser.me/) - Zufällige User-Daten
### Tools
- [Postman](https://www.postman.com/) - API Testing
- [Firebase Console](https://console.firebase.google.com/) - Firebase Management
- [Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools) - Browser Debugging