API (Application Programming Interface)
Eine definierte Schnittstelle, über die Softwaresysteme miteinander kommunizieren können – der Standard für die Integration von KI-Diensten in Anwendungen.
Eine Operation ist idempotent, wenn sie beliebig oft ausgeführt werden kann und immer dasselbe Ergebnis liefert wie beim ersten Mal – entscheidend für robuste APIs und verteilte Systeme.
In verteilten Systemen passieren Netzwerkfehler. Eine Anfrage wird gesendet, aber die Antwort kommt nie an – war die Anfrage erfolgreich oder nicht? Die sichere Lösung: einfach nochmal senden. Aber nur, wenn die Operation idempotent ist.
Nicht idempotent: POST /orders – zweimal gesendet = zwei Bestellungen
Idempotent: PUT /orders/123/status mit {"status": "shipped"} – zehnmal gesendet = Status ist „shipped”
Das ist der Kern: Idempotente Operationen können sicher wiederholt werden, ohne unerwünschte Nebeneffekte.
In verteilten Systemen gibt es drei Delivery-Garantien:
| Garantie | Bedeutung | Risiko |
|---|---|---|
| At-most-once | Höchstens einmal geliefert | Datenverlust möglich |
| At-least-once | Mindestens einmal geliefert | Duplikate möglich |
| Exactly-once | Genau einmal geliefert | Sehr aufwändig, selten wirklich garantiert |
At-least-once + Idempotenz = sicheres System: Wenn Duplikate sicher ignoriert werden, ist At-least-once ausreichend und viel einfacher als Exactly-once.
| Methode | Idempotent | Sicher | Typische Verwendung |
|---|---|---|---|
| GET | ✅ | ✅ | Daten lesen |
| PUT | ✅ | ❌ | Ressource ersetzen |
| DELETE | ✅ | ❌ | Ressource löschen |
| POST | ❌ | ❌ | Ressource erstellen |
| PATCH | ⚠️ | ❌ | Teilweise aktualisieren |
import json
import redis
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
cache = redis.Redis()
@app.post("/payments")
async def create_payment(
payment: PaymentRequest,
idempotency_key: str = Header(..., alias="Idempotency-Key")
):
cache_key = f"idem:{idempotency_key}"
# Bereits verarbeitet? → Gespeichertes Ergebnis zurückgeben
cached = cache.get(cache_key)
if cached:
return json.loads(cached)
# Noch nicht verarbeitet → Zahlung durchführen
try:
result = await process_payment(payment)
except Exception as e:
# Fehler NICHT cachen – Retry soll erneut versuchen
raise HTTPException(status_code=500, detail=str(e))
# Ergebnis 24h cachen (nur bei Erfolg)
cache.setex(cache_key, 86400, json.dumps(result))
return result
-- ❌ Nicht idempotent: Jedes Mal wird ein neuer Datensatz erstellt
INSERT INTO orders (user_id, product_id, amount)
VALUES (123, 456, 99.99);
-- ✅ Idempotent: Nur einfügen wenn noch nicht vorhanden
INSERT INTO orders (id, user_id, product_id, amount)
VALUES ('ord_abc123', 123, 456, 99.99)
ON CONFLICT (id) DO NOTHING;
-- ✅ Idempotent: Upsert – einfügen oder aktualisieren
INSERT INTO order_status (order_id, status, updated_at)
VALUES ('ord_abc123', 'shipped', NOW())
ON CONFLICT (order_id) DO UPDATE SET
status = EXCLUDED.status,
updated_at = EXCLUDED.updated_at;
processed_events = set() # In Production: Redis oder DB
@app.post("/webhooks/stripe")
async def handle_stripe_webhook(request: Request):
payload = await request.body()
sig = request.headers.get("Stripe-Signature")
# Signatur verifizieren
event = stripe.Webhook.construct_event(payload, sig, WEBHOOK_SECRET)
# Bereits verarbeitet?
if event.id in processed_events:
return {"status": "already_processed"}
# Verarbeiten
if event.type == "payment_intent.succeeded":
await fulfill_order(event.data.object)
processed_events.add(event.id)
return {"status": "ok"}
curl https://api.stripe.com/v1/charges \
-H "Idempotency-Key: a8f3b2c1-4d5e-6f7a-8b9c-0d1e2f3a4b5c" \
-d amount=2000 \
-d currency=eur
# Zweiter Aufruf mit gleichem Key → gleiche Antwort, keine zweite Zahlung
Idempotenz ist wie ein Lichtschalter: Wenn das Licht an ist und du drückst zehnmal auf 'An', bleibt es an – das Ergebnis ändert sich nicht. Aber 'Licht umschalten' ist nicht idempotent: Jedes Drücken ändert den Zustand.
Idempotente Operationen können sicher wiederholt werden – bei Netzwerkfehlern kein Problem
HTTP: GET, PUT, DELETE sind idempotent – POST in der Regel nicht
Idempotency Keys ermöglichen idempotente POST-Anfragen
Zahlungsabwicklung
Doppelte Zahlungen verhindern: Gleiche Anfrage zweimal gesendet → Zahlung nur einmal ausgeführt
Webhook-Verarbeitung
Webhooks können mehrfach gesendet werden – idempotente Handler verarbeiten sie nur einmal
Retry-Logik
Bei Netzwerkfehlern kann eine Anfrage sicher wiederholt werden, ohne Duplikate zu erzeugen
GET: Ja (liest nur). PUT: Ja (setzt Ressource auf definierten Zustand). DELETE: Ja (Ressource ist danach weg, egal wie oft). PATCH: Nicht zwingend (hängt von der Implementierung ab). POST: Nein (erstellt jedes Mal eine neue Ressource).
Eine eindeutige ID, die der Client mit einer Anfrage mitschickt. Der Server speichert das Ergebnis der ersten Anfrage mit dieser ID. Bei einer Wiederholung gibt er das gespeicherte Ergebnis zurück, ohne die Operation erneut auszuführen. Stripe und andere Payment-APIs nutzen dieses Muster.
Eine 'sichere' Operation verändert den Zustand nicht (GET, HEAD). Eine idempotente Operation kann den Zustand verändern, aber mehrfaches Ausführen hat denselben Effekt wie einmaliges (PUT, DELETE). Alle sicheren Operationen sind auch idempotent, aber nicht umgekehrt.