2026-05-14 12:40:21 +02:00

204 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* api.js Zentraler Service für alle API-Aufrufe
* ================================================
*
* Dieses Modul kapselt die gesamte Kommunikation mit der REST-API.
* Die UI-Komponenten importieren nur die benötigten Funktionen
* sie müssen nicht wissen, wie die Fetch-Aufrufe intern funktionieren.
*
* API-Basis: https://webdev.iten-web.ch/10003/api
*
* Best-Practice-Routing mit REST-Pfaden:
* GET https://webdev.iten-web.ch/10003/api/todos?page=1&limit=10
* GET https://webdev.iten-web.ch/10003/api/todos/42
*/
/** Basis-URL der API hier zentral änderbar */
const BASE_URL = 'https://webdev.iten-web.ch/10003/api'
/**
* buildUrl Erstellt eine vollständige Anfrage-URL.
*
* @param {string} path - API-Pfad, z.B. "/todos" oder "/todos/5"
* @param {object} [params] - Optionale Query-Parameter als Schlüssel-Wert-Objekt
* @returns {string} - Fertige URL als String
*
* Beispiel:
* buildUrl('/todos', { page: 2, limit: 10 })
* → "https://webdev.iten-web.ch/10003/api/todos?page=2&limit=10"
*/
function buildUrl(path, params = {}) {
const normalizedPath = path.startsWith('/') ? path : `/${path}`
const url = new URL(`${BASE_URL}${normalizedPath}`)
// Weitere Parameter anhängen (leere Werte werden übersprungen)
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== '') {
url.searchParams.set(key, value)
}
}
return url.toString()
}
// =============================================================
// GET /todos Liste abrufen (mit Pagination)
// =============================================================
/**
* fetchTodos Ruft eine paginierte Liste von ToDos ab.
*
* @param {number} [page=1] - Seitennummer (1-basiert)
* @param {number} [limit=10] - Anzahl Einträge pro Seite
* @returns {Promise<Array>} - Array von Todo-Objekten
*
* Todo-Objekt: { id, userId, title, completed }
*/
export async function fetchTodos(page = 1, limit = 10) {
const url = buildUrl('/todos', { page, limit })
const response = await fetch(url)
// HTTP-Fehler (4xx, 5xx) werden von fetch() NICHT automatisch geworfen
// wir prüfen response.ok manuell und werfen ggf. einen Fehler
if (!response.ok) {
throw new Error(`Fehler beim Laden der ToDos (HTTP ${response.status})`)
}
// response.json() liest den Response-Body und parst ihn als JSON
return response.json()
}
// =============================================================
// GET /todos/{id} Einzelnes ToDo abrufen
// =============================================================
/**
* fetchTodoById Ruft ein einzelnes ToDo per ID ab.
*
* @param {number} id - ID des gesuchten ToDos
* @returns {Promise<object>} - Todo-Objekt
*/
export async function fetchTodoById(id) {
const url = buildUrl(`/todos/${id}`)
const response = await fetch(url)
if (!response.ok) {
throw new Error(`ToDo #${id} nicht gefunden (HTTP ${response.status})`)
}
return response.json()
}
// =============================================================
// POST /todos Neues ToDo erstellen
// =============================================================
/**
* createTodo Erstellt ein neues ToDo.
*
* @param {{ userId: number, title: string, completed?: boolean }} todo
* @returns {Promise<object>} - Erstelltes ToDo-Objekt (inkl. vergebener id)
*
* Beispiel-Aufruf:
* createTodo({ userId: 1, title: "Einkaufen gehen" })
*/
export async function createTodo(todo) {
const url = buildUrl('/todos')
const response = await fetch(url, {
method: 'POST',
headers: {
// Teilt dem Server mit, dass wir JSON senden
'Content-Type': 'application/json',
},
// JSON.stringify() wandelt das JavaScript-Objekt in einen JSON-String um
body: JSON.stringify(todo),
})
if (!response.ok) {
throw new Error(`Fehler beim Erstellen des ToDos (HTTP ${response.status})`)
}
// Der Server antwortet mit 201 Created + dem neuen Objekt
return response.json()
}
// =============================================================
// PATCH /todos/{id} ToDo teilweise aktualisieren
// =============================================================
/**
* patchTodo Aktualisiert einzelne Felder eines ToDos.
*
* Im Unterschied zu PUT werden bei PATCH nur die angegebenen Felder
* geändert alle anderen bleiben unverändert.
*
* @param {number} id - Todo-ID
* @param {{ userId?: number, title?: string, completed?: boolean }} changes
* @returns {Promise<object>} - Aktualisiertes ToDo
*
* Beispiel-Aufruf (nur completed ändern):
* patchTodo(5, { completed: true })
*/
export async function patchTodo(id, changes) {
const url = buildUrl(`/todos/${id}`)
const response = await fetch(url, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(changes),
})
if (!response.ok) {
throw new Error(`Fehler beim Aktualisieren von ToDo #${id} (HTTP ${response.status})`)
}
return response.json()
}
// =============================================================
// PUT /todos/{id} ToDo vollständig ersetzen
// =============================================================
/**
* putTodo Ersetzt ein ToDo vollständig (alle Felder müssen angegeben werden).
*
* @param {number} id - Todo-ID
* @param {{ userId: number, title: string, completed: boolean }} todo
* @returns {Promise<object>} - Ersetztes ToDo
*/
export async function putTodo(id, todo) {
const url = buildUrl(`/todos/${id}`)
const response = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(todo),
})
if (!response.ok) {
throw new Error(`Fehler beim Ersetzen von ToDo #${id} (HTTP ${response.status})`)
}
return response.json()
}
// =============================================================
// DELETE /todos/{id} ToDo löschen
// =============================================================
/**
* deleteTodo Löscht ein ToDo anhand seiner ID.
*
* @param {number} id - Todo-ID
* @returns {Promise<void>} - Kein Rückgabewert (204 No Content)
*/
export async function deleteTodo(id) {
const url = buildUrl(`/todos/${id}`)
const response = await fetch(url, {
method: 'DELETE',
})
if (!response.ok) {
throw new Error(`Fehler beim Löschen von ToDo #${id} (HTTP ${response.status})`)
}
// DELETE gibt HTTP 204 No Content zurück → kein response.json() nötig
}