TS
Thomas Schmitz

Freiberuflicher IT-Berater, Softwareentwickler & DevOps. Praxisnahe Artikel zu API-Design, Architektur und Cloud-Workflows.

Serie: API-Design · Teil 3 von 22

API-Design Teil 3: API-Stil & Grundprinzipien

REST, RPC oder GraphQL? Und welche Konsistenzregeln gelten überall.

Praxisnah Checkliste

Die Wahl des API-Stils ist eine der ersten Entscheidungen, die du triffst – und eine der langlebigsten. REST, RPC, GraphQL oder ein Hybrid: Jeder Ansatz hat Trade-offs, und keiner ist objektiv besser. Entscheidend ist, dass du bewusst wählst und dann konsistent bleibst.

Dieser Artikel hilft dir, den passenden Stil für deinen Kontext zu wählen und Grundprinzipien zu definieren, die für alle Endpoints gelten. Am Ende hast du einen Stil-ADR (Architecture Decision Record), der deine Wahl dokumentiert und als Referenz für das Team dient.

Zielbild

Nach diesem Artikel kannst du:

  • Die Vor- und Nachteile von REST, RPC und GraphQL einordnen
  • Einen API-Stil für dein Projekt begründet wählen
  • Konsistenzregeln definieren, die für alle Endpoints gelten
  • Das Prinzip Least Surprise auf dein API-Design anwenden
  • Idempotenz und Backward Compatibility als Designziele verankern

Kernfragen

Bevor du weiterliest, versuche diese Fragen für dein Projekt zu beantworten:

  1. Welcher Stil passt zu unseren Use Cases? CRUD-lastig, aktionsorientiert, flexible Queries?
  2. Welche Regeln gelten überall? Naming, Fehlerformat, Datumsformat, Pagination?
  3. Wie garantieren wir Idempotenz? Welche Operationen sind sicher wiederholbar?
  4. Wie schützen wir Backward Compatibility? Was ist ein Breaking Change, was nicht?
  5. Wer definiert und enforced die Standards? Dokumentation, Linting, Reviews?

API-Stile im Vergleich

REST (Representational State Transfer)

REST modelliert APIs als Ressourcen, die über HTTP-Methoden manipuliert werden. Es ist der De-facto-Standard für öffentliche APIs.

Stärken:

  • Weit verbreitet, gut verstanden
  • HTTP-Caching out of the box
  • Klare Semantik durch HTTP-Methoden
  • Gute Tooling-Unterstützung (OpenAPI, Postman, etc.)

Schwächen:

  • Over-fetching und Under-fetching bei komplexen Datenstrukturen
  • Aktionen, die nicht in CRUD passen, werden unelegant
  • Mehrere Roundtrips für zusammenhängende Daten

Passt gut für: CRUD-lastige APIs, öffentliche APIs, APIs mit Caching-Anforderungen.

RPC (Remote Procedure Call)

RPC modelliert APIs als Funktionsaufrufe. Endpoints repräsentieren Aktionen, nicht Ressourcen.

Stärken:

  • Natürlich für aktionsorientierte Use Cases
  • Klare Benennung: POST /orders/{id}/cancel statt PATCH /orders/{id}
  • Einfacher bei komplexen Operationen

Schwächen:

  • Kein standardisiertes Caching
  • Weniger Konsistenz, wenn nicht strikt enforced
  • HTTP-Semantik wird oft ignoriert

Passt gut für: Interne Services, aktionsorientierte APIs, Workflow-getriebene Anwendungen.

GraphQL

GraphQL ist eine Query-Sprache, bei der Clients genau die Daten anfordern, die sie brauchen.

Stärken:

  • Keine Over-/Under-fetching-Probleme
  • Typischerweise ein zentraler Endpoint
  • Starke Typisierung und Introspection
  • Gut für Frontend-getriebene Entwicklung

Schwächen:

  • Komplexeres Caching (klassisches HTTP-Caching selten von Haus aus)
  • Höhere Einstiegshürde für Clients
  • N+1-Query-Probleme auf Serverseite
  • Schwieriger zu rate-limiten

Passt gut für: Datenintensive Frontends, Mobile Apps mit variablen Bandbreiten, APIs mit stark vernetzten Daten.

Hybrid

In der Praxis kombinieren viele APIs Stile: REST für CRUD, RPC-artige Endpoints für Aktionen, vielleicht GraphQL für flexible Queries.

Regel: Hybrid ist legitim, aber dokumentiere klar, wann welcher Stil gilt. Inkonsistenz ohne Begründung ist das Problem, nicht der Mix selbst.

Konsistenzregeln definieren

Egal welchen Stil du wählst – Konsistenz ist wichtiger als die Wahl selbst. Definiere Regeln, die für alle Endpoints gelten.

Naming Conventions

Aspekt Regel Beispiel
URL-Pfade Kebab-case, Plural für Collections /order-items, nicht /orderItems
Query-Parameter snake_case ?page_size=20
JSON-Felder snake_case created_at, nicht createdAt
Ressourcen-IDs Nicht erratbar, stabil UUIDs oder Hashids, keine sequentiellen IDs

Anti-Pattern: Naming je nach Endpoint unterschiedlich. Das erzeugt kognitive Last bei Konsumenten.

Fehlerformat

Definiere ein einheitliches Fehlerformat für alle Endpoints. Details folgen in Teil 8, aber die Grundstruktur sollte früh feststehen:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      {
        "field": "email",
        "reason": "Invalid email format"
      }
    ],
    "request_id": "req_abc123"
  }
}

Regel: Jeder 4xx/5xx-Response folgt diesem Format. Keine Ausnahmen.

Datumsformat

Verwende ISO 8601 mit Zeitzone, immer:

{
  "created_at": "2026-01-24T14:30:00Z",
  "updated_at": "2026-01-24T15:45:00+01:00"
}

Anti-Pattern: Unix-Timestamps, lokale Formate oder Datumsstrings ohne Zeitzone.

Pagination

Wähle ein Pagination-Modell und bleib dabei. Details folgen in Teil 7, aber entscheide früh:

  • Offset-based: ?offset=20&limit=10 – einfach, aber problematisch bei großen Datasets
  • Cursor-based: ?cursor=abc123&limit=10 – stabiler, aber komplexer

Regel: Alle List-Endpoints nutzen dasselbe Pagination-Modell.

Prinzip: Least Surprise

APIs sollten sich so verhalten, wie Entwickler es erwarten. Least Surprise bedeutet:

  • HTTP-Methoden korrekt nutzen: GET liest, POST erstellt oder triggert nicht-idempotente Aktionen, PUT ersetzt, PATCH aktualisiert, DELETE löscht.
  • Statuscodes korrekt nutzen: 200 für Erfolg, 201 für Created, 204 für No Content, 400 für Client-Fehler, 500 für Server-Fehler.
  • Keine versteckten Seiteneffekte: Ein GET darf keine Daten verändern. Ein DELETE löscht nur, was im Pfad steht.
  • Konsistente Benennung: Wenn ein Feld created_at heißt, sollte das überall so sein.

Anti-Pattern: GET-Requests, die Daten verändern (z.B. GET /users/{id}/activate). Das bricht Caching und Erwartungen.

Idempotenz garantieren

Eine Operation ist idempotent, wenn sie bei mehrfacher Ausführung dasselbe Ergebnis liefert. Idempotenz ist kritisch für Robustheit – Clients müssen Requests bei Timeouts sicher wiederholen können.

Idempotenz nach HTTP-Methode

Methode Idempotent? Anmerkung
GET Ja Liest nur, verändert nichts
PUT Ja Ersetzt Ressource vollständig
DELETE Ja Zweites DELETE liefert 404, aber Zustand ist gleich
POST Nein (meist) Kann idempotent gemacht werden mit Idempotency-Key
PATCH Kommt drauf an Inkrementelle Updates (+1) sind nicht idempotent

Idempotency-Keys

Für nicht-idempotente Operationen (z.B. Zahlungen) verwende Idempotency-Keys:

POST /payments
Idempotency-Key: pay_abc123
Content-Type: application/json

{ "amount": 100, "currency": "EUR" }

Der Server speichert das Ergebnis unter diesem Key und gibt bei Wiederholung dasselbe zurück.

Regel: Dokumentiere klar, welche Endpoints Idempotency-Keys unterstützen und wie lange sie gültig sind.

Backward Compatibility schützen

Breaking Changes sind teuer – besonders bei externen Konsumenten. Definiere früh, was ein Breaking Change ist und wie du ihn vermeidest.

Was ist ein Breaking Change?

Änderung Breaking?
Neues optionales Feld hinzufügen Nein
Pflichtfeld im Request optional machen Nein (macht Requests toleranter)
Request-Feld entfernen (nicht mehr akzeptiert) Ja
Neues Pflichtfeld hinzufügen Ja
Feldtyp ändern (string → number) Ja
Enum-Wert entfernen Ja
Endpoint entfernen Ja
Fehlercode ändern Ja
URL-Struktur ändern Ja

Strategien für Kompatibilität

  • Additive Changes: Neue Felder sind optional, alte bleiben erhalten.
  • Tolerant Reader: Clients ignorieren unbekannte Felder.
  • Deprecation vor Removal: Felder/Endpoints erst deprecated, dann nach Frist entfernt.
  • Versionierung: Bei unvermeidlichen Breaking Changes neue Version einführen.

Anti-Pattern: Das benutzt eh niemand mehr – ohne Metriken ist das eine gefährliche Annahme.

Regeln & Anti-Patterns

Do

  • Wähle einen API-Stil bewusst und dokumentiere die Begründung
  • Definiere Konsistenzregeln vor dem ersten Endpoint
  • Nutze HTTP-Methoden und Statuscodes korrekt
  • Mache idempotente Operationen explizit
  • Behandle Backward Compatibility als Designziel, nicht als Nachgedanken

Don't

  • Stil je nach Endpoint wechseln ohne klare Regel
  • Naming, Fehlerformat oder Pagination pro Endpoint neu erfinden
  • GET für Zustandsänderungen missbrauchen
  • Breaking Changes ohne Vorwarnung einführen
  • Idempotenz ignorieren (Clients sollen halt nicht doppelt senden)

Artefakt: Stil-ADR

Dokumentiere deine Stilentscheidung in einem Architecture Decision Record:

# ADR: API-Stil und Grundprinzipien

## Status

Accepted

## Kontext

Wir bauen eine API für [Zielgruppe/Use Case]. Die API wird von [internen
Teams / Partnern / öffentlichen Entwicklern] konsumiert.

## Entscheidung

Wir verwenden **[REST / RPC / GraphQL / Hybrid]** als API-Stil.

Begründung:

- [Grund 1, z.B. CRUD-lastige Use Cases passen gut zu REST]
- [Grund 2, z.B. HTTP-Caching ist wichtig für Performance]
- [Grund 3, z.B. Breite Tooling-Unterstützung für Partner]

## Konsistenzregeln

| Aspekt          | Regel                                                          |
|-----------------|----------------------------------------------------------------|
| URL-Pfade       | Kebab-case, Plural für Collections                             |
| Query-Parameter | snake_case                                                     |
| JSON-Felder     | snake_case                                                     |
| Datumsformat    | ISO 8601 mit Zeitzone (UTC bevorzugt)                          |
| Fehlerformat    | Einheitliches JSON-Schema (siehe Error-Katalog)                |
| Pagination      | Cursor-based mit `cursor` und `limit`                          |
| Idempotenz      | POST-Endpoints mit Seiteneffekten unterstützen Idempotency-Key |

## Konsequenzen

- Alle neuen Endpoints folgen diesen Regeln
- Abweichungen erfordern Review und Dokumentation
- Bestehende Endpoints werden bei nächster Gelegenheit migriert

Checkliste

Bevor du zum nächsten Artikel gehst, prüfe:

  • [ ] API-Stil ist gewählt und begründet
  • [ ] Naming Conventions sind definiert (URLs, Query-Params, JSON-Felder)
  • [ ] Fehlerformat ist festgelegt
  • [ ] Datumsformat ist festgelegt (ISO 8601)
  • [ ] Pagination-Modell ist gewählt
  • [ ] HTTP-Methoden und Statuscodes werden korrekt verwendet
  • [ ] Idempotenz-Strategie ist dokumentiert
  • [ ] Breaking-Change-Definition existiert
  • [ ] Stil-ADR ist ausgefüllt

Wie es weitergeht

Im nächsten Teil geht es um Ressourcenmodellierung: Welche Substantive werden zu Ressourcen? Wie stabil müssen IDs sein? Und wie tief darf Nesting gehen?

Alle Teile der Serie: Serie: API-Design

Mehr Beiträge aus dem Blog.