Eine API ist der „Vertrag“ zwischen Systemen: Sie definiert, welche Funktionen angeboten werden, wie man sie aufruft und welche Datenformate dabei gelten. Gute APIs reduzieren Integrationsaufwand, erhöhen Sicherheit und machen Systeme skalierbar – schlechte APIs erzeugen dauerhaft Kosten.
Merksatz: Eine API ist nicht „nur ein Endpoint“ – sie ist ein stabiler Vertrag, der Änderungen kontrollierbar macht.
„API“ (Application Programming Interface) bezeichnet eine definierte Schnittstelle, über die Software miteinander interagiert. Das kann ein Web-Service (HTTP), eine Bibliothek (z.B. .NET-Assembly), ein Betriebssystem-Interface oder auch ein internes Service-Protokoll sein.
In der Praxis dominieren drei Stile. Keiner ist „immer besser“ – entscheidend ist die Systemlandschaft und der Use Case.
| Stil | Stärken | Typische Nutzung | Risiken/Trade-offs |
|---|---|---|---|
| REST (HTTP/JSON) | Einfach, breit unterstützt, caching-freundlich, gut debuggbar | Public APIs, klassische Web-Backends, Microservices | Over/Under-Fetching möglich, viele Endpoints |
| GraphQL | Client bestimmt Datenform, weniger Requests, stark für komplexe UI | Apps mit sehr variablen Datenbedarfen | Caching/Rate-Limits komplexer, Query-Kosten kontrollieren |
| gRPC (Protobuf) | Sehr schnell, starke Typisierung, Streaming, guter Vertrag | Interne Service-to-Service-Kommunikation | Browser-Unterstützung indirekt, Tooling/Observability beachten |
REST modelliert Dinge als Ressourcen (z.B. /users, /orders). Die HTTP-Methoden drücken aus, was passieren soll:
GET lesen (idempotent)POST erzeugen/auslösen (nicht idempotent, kann aber idempotent gestaltet werden)PUT vollständig ersetzen (idempotent)PATCH partiell ändernDELETE löschen (idempotent)Konsistente Statuscodes sind ein Qualitätsmerkmal. Clients müssen sich darauf verlassen können, was ein Code bedeutet.
| Code | Bedeutung | Wann verwenden? |
|---|---|---|
| 200 | OK | Erfolgreiches Lesen/Ändern mit Response |
| 201 | Created | Ressource wurde erstellt (Location-Header sinnvoll) |
| 204 | No Content | Erfolg ohne Response-Body |
| 400 | Bad Request | Request syntaktisch/strukturell falsch |
| 401 | Unauthorized | Keine/ungültige Authentifizierung |
| 403 | Forbidden | Authentifiziert, aber keine Berechtigung |
| 404 | Not Found | Ressource existiert nicht |
| 409 | Conflict | Konflikt (z.B. Versionskonflikt, Duplicate) |
| 429 | Too Many Requests | Rate Limit erreicht |
| 500 | Server Error | Unerwarteter interner Fehler (ohne Details zu leaken) |
API-Sicherheit beginnt nicht bei „Token prüfen“, sondern beim Gesamtdesign: Transport, Identität, Rechte, Input-Validation, Limits und Logging.
Wichtig: Authentifizierung = „Wer bist du?“. Autorisierung = „Darfst du das?“. Beides getrennt denken.
Eine API ist ein Vertrag. Breaking Changes ohne Migrationspfad zerstören Vertrauen und erzeugen Support-Aufwand. Saubere Versionierung sorgt dafür, dass du erweitern kannst, ohne Integrationen zu brechen.
/v1/orders – klar, einfach.X-Api-Version: 1 – flexibel, aber weniger sichtbar.application/vnd.company.v1+json – präzise, mehr Aufwand.Ergänzend: Deprecation-Policy (Fristen), Changelog, Migrationsguide, Vertragstests (Consumer-Driven Contracts).
Fehlermeldungen müssen für Menschen verständlich und für Clients stabil parsebar sein. Bewährt ist ein standardisiertes Fehlerobjekt mit Code, Message, Details, Correlation ID.
HTTP/1.1 400 Bad Request Content-Type: application/json { "error": { "code": "VALIDATION_FAILED", "message": "Ein Feld ist ungültig.", "details": [ { "field": "email", "issue": "Ungültiges Format" } ], "correlationId": "b6f1c3e5-7a2b-4f4c-9b9f-9a1d4c2d3b10" } }
Sobald Listen groß werden, brauchst du klare Regeln. Für stabile Ergebnisse ist Cursor-Pagination oft besser als Offset.
?status=active&country=DE?sort=-createdAt (Minus = desc)?limit=50&cursor=...Robustheit entsteht durch Grenzen. Ohne Limits können einzelne Clients das System destabilisieren.
POST, damit Retries nicht doppelt buchen/erstellen.
HTTP bietet starke Mechanismen: Cache-Control, ETag, If-None-Match. Für Read-heavy Endpoints kann das massiv Kosten senken.
Eine gute API ist dokumentiert, versioniert und testbar. OpenAPI/Swagger macht Endpoints, Modelle, Auth-Schemes und Beispiele maschinenlesbar – und ermöglicht Mocking sowie automatisierte Tests.
In Produktion zählen Metriken, Logs und Traces. Mit Correlation IDs und verteiltem Tracing findest du Ursachen statt Symptome.
curl -sS \ -H "Authorization: Bearer" \ -H "Accept: application/json" \ "https://api.example.com/v1/orders?status=active&limit=25"
app.MapPost("/v1/orders", (CreateOrderDto dto, HttpContext ctx) => { if (string.IsNullOrWhiteSpace(dto.CustomerId)) return Results.BadRequest(new { error = new { code = "VALIDATION_FAILED", message = "CustomerId ist erforderlich.", correlationId = ctx.TraceIdentifier } }); // TODO: persist order return Results.Created($"/v1/orders/{id}", new { id = "ord_123" }); });
import rateLimit from "express-rate-limit"; import express from "express"; const app = express(); app.use(express.json()); app.use(rateLimit({ windowMs: 60_000, max: 60, standardHeaders: true, legacyHeaders: false })); app.get("/v1/health", (req, res) => { res.json({ status: "ok" }); });
Eine professionelle API ist ein stabiler Vertrag mit sauberer Versionierung, konsistentem Fehlerhandling, klarer Auth/Autorisierung, messbarer Performance und guter Dokumentation. Wenn diese Grundlagen sitzen, werden Systeme wartbarer, Integrationen schneller und Betriebskosten deutlich niedriger.