API-Design für einen Cloud Service Provider: Ein praktischer Leitfaden

2026-07-26

Wer eine API für einen Cloud Service Provider (CSP) baut, steht vor einer anderen Herausforderung als bei einer klassischen Web-API: Cloud-Ressourcen sind langlebig, Operationen dauern oft Minuten statt Millisekunden, und Kunden verlassen sich auf diese API, um produktive Infrastruktur zu steuern. Ein Fehler im Design lässt sich später kaum noch reparieren, ohne bestehende Nutzer zu brechen. Dieser Beitrag beschreibt die wichtigsten Entscheidungen, die man von Anfang an richtig treffen sollte, inklusive konkreter Beispiele.

Spec-First statt Code-First

Der wichtigste Grundsatz: Die API-Spezifikation kommt zuerst, der Code danach. Konkret heißt das, eine OpenAPI-3.1-Spezifikation zu schreiben, bevor überhaupt ein Server-Endpunkt implementiert wird.

Das bringt mehrere Vorteile:

Praktischer Workflow

Ein bewährter Ablauf sieht so aus:

  1. Spec in einem Repository versionieren (ein eigenes api-spec-Repo, getrennt vom Server-Code)
  2. Bei jedem Pull Request automatisch gegen Spectral-Regeln linten (CI-Pipeline)
  3. Bei jedem Merge automatisch Client-SDKs generieren und als eigene Pakete veröffentlichen (z. B. npm, PyPI)
  4. Breaking Changes durch ein Diff-Tool wie oasdiff automatisch erkennen und den Merge blockieren, falls keine neue Major-Version vergeben wurde

Ein minimaler Auszug einer solchen Spec für eine Instance-Ressource:

paths:
  /v1/instances:
    post:
      summary: Instanz erstellen
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateInstanceRequest'
      responses:
        '202':
          description: Operation gestartet
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Operation'
components:
  schemas:
    CreateInstanceRequest:
      type: object
      required: [name, region, size]
      properties:
        name:
          type: string
        region:
          type: string
        size:
          type: string
          enum: [small, medium, large]
        tags:
          type: object
          additionalProperties:
            type: string

Ressourcenmodell: Was eine CSP-API typischerweise abbildet

Die Kernressourcen unterscheiden sich je nach Angebot, aber ein typisches Set sieht so aus:

Jede dieser Ressourcen sollte konsistenten REST-Konventionen folgen: gleiche Pluralisierung, gleiche Antwortformate, gleiches Verhalten bei Filterung und Sortierung.

Konsistenz-Checkliste für Ressourcen

Bevor eine neue Ressource in die API aufgenommen wird, lohnt sich eine kurze Checkliste:

AspektKonvention
NamensgebungImmer Plural: /instances, nicht /instance
IDsImmer als eigenständiges Feld id, niemals im Pfad als einziger Identifikator ohne Ressourcen-Präfix
ZeitstempelImmer created_at / updated_at in ISO-8601, UTC
Soft-Delete vs. Hard-DeleteKonsistent für alle Ressourcen gleich handhaben
StatusfeldEinheitliches Enum-Namensschema über alle Ressourcen (pending, active, error, deleting)
Filter-ParameterGleiche Query-Parameter-Syntax überall (?status=active&region=eu-central)

Asynchrone Operationen von Anfang an einplanen

Das Erstellen einer VM oder das Ziehen eines Snapshots dauert Sekunden bis Minuten. Eine CSP-API sollte das nicht wie eine synchrone Operation behandeln:

POST /v1/instances
→ 202 Accepted
{
  "operation_id": "op-8f2a1c",
  "status": "pending"
}

Der Client pollt anschließend den Operations-Endpunkt:

GET /v1/operations/op-8f2a1c
→ { "status": "running" }
→ { "status": "done", "resource": { ... } }

Ergänzend dazu bieten Webhooks eine push-basierte Alternative, damit nicht jeder Client aktiv pollen muss. Wer es interaktiver braucht, kann zusätzlich Server-Sent Events oder WebSockets für Live-Status anbieten.

Operation-Objekt im Detail

Ein robustes Operation-Schema enthält mehr als nur den Status:

{
  "operation_id": "op-8f2a1c",
  "type": "instance.create",
  "status": "running",
  "progress_percent": 60,
  "created_at": "2026-07-27T10:15:00Z",
  "started_at": "2026-07-27T10:15:02Z",
  "finished_at": null,
  "target_resource": {
    "type": "instance",
    "id": "inst-a91b2"
  },
  "error": null
}

Wichtig ist außerdem eine klare Antwort auf die Frage: Was passiert bei einem Fehler mitten in der Operation? Zwei gängige Strategien:

Für die meisten CSP-APIs ist Fail-Forward einfacher zu implementieren und transparenter für den Nutzer, da ein automatischer Rollback selbst wieder scheitern kann.

Webhooks richtig absichern

Wer Webhooks anbietet, sollte von Anfang an:

Authentifizierung und Autorisierung

Für eine CSP-API haben sich zwei Modelle etabliert, die sich auch kombinieren lassen:

Für die Autorisierung innerhalb eines Kontos empfiehlt sich ein RBAC-Modell (Role-Based Access Control) mit möglichst feingranularen Permissions, zum Beispiel:

instances:read
instances:create
instances:delete
billing:read
iam:manage

Rollen wie viewer, operator, admin bündeln dann Sets dieser Permissions. Wichtig: Permissions sollten von Anfang an pro Ressourcen-Typ und Aktion granular genug sein. Ein nachträgliches Aufsplitten einer zu groben Permission ist sonst ein Breaking Change für alle bestehenden Rollenzuweisungen.

Zusätzlich lohnt sich früh die Einführung von Scoped API-Keys, die nur für bestimmte Ressourcen oder Projekte gültig sind, statt eines einzigen Account-weiten Keys mit vollen Rechten.

Idempotenz ist kein Nice-to-have

Netzwerkfehler passieren. Wenn ein Client einen POST /instances-Request wiederholt, weil die Antwort nicht ankam, darf das nicht zu zwei erstellten VMs führen. Die Lösung ist ein Idempotency-Key-Header, den der Client mitschickt:

POST /v1/instances
Idempotency-Key: 6f2a-91cd-... (vom Client generierte UUID)

Der Server speichert für jeden Key das Ergebnis des ersten Requests (typischerweise 24 Stunden) und gibt bei einem identischen Wiederholungsrequest dieselbe Antwort zurück, ohne die Operation erneut auszuführen. Wichtig ist dabei:

Dieses Muster hat sich unter anderem bei Stripes API bewährt und lässt sich direkt übernehmen.

Pagination, Rate-Limiting und Versionierung

Pagination

Cursor-basiert ist robuster als Offset-basiert, besonders wenn sich Listen zwischen zwei Requests ändern können:

GET /v1/instances?limit=50&cursor=eyJpZCI6ImluczEyMyJ9
→ {
  "data": [...],
  "next_cursor": "eyJpZCI6ImluczE3NSJ9",
  "has_more": true
}

Bei Offset-basierter Pagination kann ein zwischenzeitlich gelöschtes Element dazu führen, dass ein Eintrag in der Liste übersprungen oder doppelt angezeigt wird. Bei Cursor-basierter Pagination passiert das nicht, da der Cursor auf eine konkrete Position im (unveränderlichen) Sortierschlüssel zeigt.

Rate-Limiting

Pro Tenant/API-Key, mit transparenten Response-Headern:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 942
X-RateLimit-Reset: 1706353200

Ein bewährter Algorithmus dafür ist Token-Bucket (erlaubt kurze Bursts, glättet aber die durchschnittliche Rate) gegenüber striktem Fixed-Window, das an Fenstergrenzen zu unfairen Spitzen führen kann. Bei 429 Too Many Requests sollte immer ein Retry-After-Header mitgeschickt werden, damit sich Clients korrekt verhalten können.

Versionierung

Pfad-basiert (/v1/...) ist für öffentliche APIs am robustesten, da Clients die Version explizit sehen und steuern können. Zusätzlich empfiehlt sich:

Fehlerformate vereinheitlichen

Ein einheitliches Fehlerschema erleichtert es Client-Entwicklern enorm, Fehler programmatisch zu behandeln. RFC 9457 (Problem Details for HTTP APIs), der aktuelle Standard, der den älteren RFC 7807 ablöst, ist ein guter Standard dafür:

{
  "type": "https://api.example.com/errors/quota-exceeded",
  "title": "Quota exceeded",
  "status": 429,
  "detail": "Instance quota of 50 reached for this account.",
  "instance": "/v1/instances",
  "trace_id": "7f3e-9a21-..."
}

Das Feld trace_id verdient besondere Erwähnung: Es sollte in jeder Fehlerantwort enthalten sein und sich im Server-seitigen Logging/Tracing (z. B. OpenTelemetry) wiederfinden. Das verkürzt Support-Anfragen erheblich, da Kunde und Support-Team über dieselbe eindeutige ID sprechen können.

Multi-Tenancy von Anfang an mitdenken

Mandantentrennung lässt sich nur schwer nachträglich in ein bestehendes Datenmodell einziehen. Übliche Ansätze, von strikt bis flexibel:

Unabhängig vom gewählten Modell gilt: Die Tenant-Zuordnung sollte niemals ausschließlich in der Applikationslogik geprüft werden, sondern zusätzlich auf Datenbankebene abgesichert sein (z. B. PostgreSQL Row-Level-Security), damit ein Bug in der Applikation nicht zu einem Datenleck zwischen Kunden führt.

Testing-Strategie

Für eine API, auf die Kunden produktive Infrastruktur aufbauen, reicht klassisches Unit-Testing nicht aus:

Fazit

Eine CSP-API zu bauen bedeutet, von Anfang an für Langlebigkeit zu designen: asynchrone Operationen, Idempotenz, saubere Versionierung, granulare Autorisierung und ein konsistentes Fehlerformat sind keine Details, die man “später noch ergänzt”. Wer diese Grundlagen von Tag eins an einplant, spart sich schmerzhafte Breaking Changes, sobald die ersten Kunden produktiv auf der API arbeiten.

← Zurück zur Übersicht

Kommentare