Tutorial · Einsteiger
REST API Tutorial (Deutsch): von Null zur ersten eigenen API
REST ist der De-facto-Standard, wie sich Programme über HTTP unterhalten. In diesem Tutorial lernst du die Grundlagen – HTTP-Methoden, Status-Codes, CRUD – und baust am Ende eine lauffähige API mit Node.js und Express.
Was ist eine REST API?
REST (Representational State Transfer) ist ein Architekturstil für Web-Schnittstellen. Statt eigener Befehle nutzt eine REST API die vorhandenen Mittel des Webs: URLs adressieren Ressourcen (z. B. Bücher, Nutzer, Bestellungen), und die HTTP-Methode legt fest, was mit der Ressource passieren soll.
Zwei Eigenschaften sind zentral:
- Ressourcen-orientiert: Eine URL wie
/api/v1/buecher/42bezeichnet genau ein Objekt. Man verwendet Substantive (Nomen) im Plural – keine Verben wie/getBuch. - Zustandslos (stateless): Jede Anfrage enthält alle Informationen, die der Server braucht. Der Server merkt sich zwischen zwei Requests nichts – das macht APIs einfach skalierbar.
HTTP-Grundlagen: Methoden, Header, Body
Eine HTTP-Anfrage besteht aus einer Methode (dem Verb), einer URL,
optionalen Headern (Metadaten wie Content-Type oder Authorization)
und – bei schreibenden Zugriffen – einem Body (meist JSON).
| Methode | Zweck | Body? | Idempotent? |
|---|---|---|---|
GET | Ressource(n) lesen | nein | ja |
POST | Neue Ressource anlegen | ja | nein |
PUT | Ressource vollständig ersetzen | ja | ja |
PATCH | Ressource teilweise ändern | ja | nein |
DELETE | Ressource löschen | nein | ja |
Idempotent bedeutet: Ein mehrfach identisch gesendeter Request hat denselben Effekt wie ein
einzelner. PUT mit gleichem Inhalt zweimal zu senden ändert nach dem ersten Mal nichts mehr –
POST hingegen würde zwei Objekte anlegen.
Status-Codes verstehen
Der Server antwortet immer mit einem dreistelligen Status-Code. Die erste Ziffer verrät die Kategorie:
| Bereich | Bedeutung | Häufige Codes |
|---|---|---|
2xx | Erfolg | 200 OK · 201 Created · 204 No Content |
3xx | Umleitung | 301 Moved Permanently · 304 Not Modified |
4xx | Client-Fehler | 400 Bad Request · 401 Unauthorized · 403 Forbidden · 404 Not Found · 429 Too Many Requests |
5xx | Server-Fehler | 500 Internal Server Error · 503 Service Unavailable |
4xx = „Du hast einen Fehler gemacht“ (falsche Eingabe, fehlende Anmeldung).
5xx = „Der Server hat einen Fehler gemacht“. Verwende Codes bewusst – ein
200 mit Fehlermeldung im Body ist schlechter Stil.
CRUD: die vier Grundoperationen
Fast jede API bildet CRUD ab – Create, Read, Update, Delete. Diese vier Operationen lassen sich direkt auf HTTP-Methoden abbilden:
| Operation | HTTP | Beispiel-Route | Erfolgs-Code |
|---|---|---|---|
| Create | POST | /api/v1/buecher | 201 |
| Read (Liste) | GET | /api/v1/buecher | 200 |
| Read (Einzeln) | GET | /api/v1/buecher/42 | 200 |
| Update | PUT | /api/v1/buecher/42 | 200 |
| Delete | DELETE | /api/v1/buecher/42 | 204 |
Hands-on: erste API mit Express 5
Jetzt bauen wir das selbst. Wir verwenden Express, das meistgenutzte Web-Framework für Node.js. Express 5 ist seit Oktober 2024 die stabile Version und setzt Node.js 18 oder neuer voraus.
Schritt 1: Projekt anlegen
mkdir buecher-api && cd buecher-api
npm init -y
npm install express
node --version # sollte v18 oder höher zeigen
Schritt 2: Server schreiben
Lege eine Datei server.js mit folgendem Inhalt an. Wir speichern die Bücher der Einfachheit
halber im Arbeitsspeicher (in echt käme hier eine Datenbank).
const express = require("express");
const app = express();
// JSON-Bodies automatisch parsen
app.use(express.json());
// "Datenbank" im Speicher
let buecher = [
{ id: 1, titel: "Clean Code", autor: "Robert C. Martin" },
{ id: 2, titel: "The Pragmatic Programmer", autor: "Hunt & Thomas" }
];
let naechsteId = 3;
// READ – alle Bücher
app.get("/api/v1/buecher", (req, res) => {
res.json(buecher);
});
// READ – ein einzelnes Buch (mit 404-Behandlung)
app.get("/api/v1/buecher/:id", (req, res) => {
const buch = buecher.find(b => b.id === Number(req.params.id));
if (!buch) {
return res.status(404).json({ error: "Buch nicht gefunden" });
}
res.json(buch);
});
// CREATE – neues Buch anlegen
app.post("/api/v1/buecher", (req, res) => {
const { titel, autor } = req.body;
if (!titel || !autor) {
return res.status(400).json({ error: "titel und autor sind Pflicht" });
}
const buch = { id: naechsteId++, titel, autor };
buecher.push(buch);
res
.status(201)
.location(`/api/v1/buecher/${buch.id}`)
.json(buch);
});
// UPDATE – Buch vollständig ersetzen
app.put("/api/v1/buecher/:id", (req, res) => {
const buch = buecher.find(b => b.id === Number(req.params.id));
if (!buch) {
return res.status(404).json({ error: "Buch nicht gefunden" });
}
const { titel, autor } = req.body;
buch.titel = titel;
buch.autor = autor;
res.json(buch);
});
// DELETE – Buch löschen
app.delete("/api/v1/buecher/:id", (req, res) => {
const index = buecher.findIndex(b => b.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: "Buch nicht gefunden" });
}
buecher.splice(index, 1);
res.status(204).end(); // 204 = Erfolg ohne Body
});
app.listen(3000, () => {
console.log("API läuft auf http://localhost:3000");
});
Schritt 3: Server starten
node server.js
# → API läuft auf http://localhost:3000
API mit curl testen
Öffne ein zweites Terminal und sprich die Endpunkte an:
# Alle Bücher abrufen (GET)
curl http://localhost:3000/api/v1/buecher
# Neues Buch anlegen (POST) – Antwort: 201 Created
curl -X POST http://localhost:3000/api/v1/buecher \
-H "Content-Type: application/json" \
-d '{"titel":"Refactoring","autor":"Martin Fowler"}'
# Ein Buch aktualisieren (PUT)
curl -X PUT http://localhost:3000/api/v1/buecher/1 \
-H "Content-Type: application/json" \
-d '{"titel":"Clean Code (2. Aufl.)","autor":"Robert C. Martin"}'
# Ein Buch löschen (DELETE) – Antwort: 204, mit -i siehst du den Status
curl -X DELETE http://localhost:3000/api/v1/buecher/1 -i
Für grafisches Testen sind Postman, Insomnia oder das quelloffene Bruno praktisch – dort kannst du Requests speichern und in Sammlungen organisieren.
Best Practices
- Nomen im Plural für Ressourcen (
/buecher, nicht/buchListe). - Versioniere deine API über den Pfad (
/api/v1/…). So kannst du später Breaking Changes einführen, ohne bestehende Clients zu zerstören. - Passende Status-Codes verwenden – besonders
201beim Anlegen,204beim Löschen und400/404bei Fehlern. - Eingaben validieren. Vertraue nie dem Client. Prüfe Pflichtfelder und Typen
(Bibliotheken wie
zododerexpress-validatorhelfen). - Konsistentes Fehlerformat, z. B. immer
{ "error": "…" }. - Große Listen paginieren (
?seite=2&limit=20) statt tausende Objekte auf einmal zu senden. - HTTPS erzwingen und schreibende Endpunkte absichern.
Ein Detail zu Express 5: Wirft ein async-Handler einen Fehler bzw. wird ein Promise
abgelehnt, wird dieser Fehler automatisch an die Fehler-Middleware weitergereicht –
das lästige try/catch um jeden Handler entfällt.
// Async-Handler ohne try/catch (Express 5 leitet den Fehler weiter)
app.get("/api/v1/status", async (req, res) => {
const daten = await ladeDaten(); // wirft ggf. einen Fehler
res.json(daten);
});
// Zentrale Fehler-Middleware – ganz am Ende definieren
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: "Interner Serverfehler" });
});
Eine REST API adressiert Ressourcen über URLs, nutzt HTTP-Methoden als Verben und antwortet mit aussagekräftigen Status-Codes. Mit rund 40 Zeilen Express hast du eine vollständige CRUD-API. Der nächste logische Schritt: die schreibenden Endpunkte absichern.
Hat dir das Tutorial geholfen?
Die Inhalte sind kostenlos. Über einen Kaffee freue ich mich sehr. ☕