21. Sept. 2025·5 Min. Lesezeit

API-Idempotenz: doppelte Erstellungen mit retry-sicherem Design verhindern

API-Idempotenz verhindert doppelte Erstellungen und Doppelbelastungen durch Request-IDs, Unique-Constraints und retry-sichere Regeln für Ihre Endpunkte.

API-Idempotenz: doppelte Erstellungen mit retry-sicherem Design verhindern

Warum doppelte Erstellungen in der Praxis passieren

Doppelte Erstellungen entstehen normalerweise nicht wegen „böser Nutzer“. Sie passieren, weil Netzwerke und Menschen unordentlich sind.

Ein Telefon in schlechtem WLAN sendet eine Anfrage, die App zeigt einen Spinner, und der Nutzer tippt noch einmal. Oder die Anfrage erreicht Ihren Server, aber die Antwort kommt nicht zurück, also versucht der Client es erneut.

Retries passieren auch automatisch. Mobile Apps, Browser-Fetch-Bibliotheken, API-Gateways und Hintergrund-Job-Runner versuchen oft nach einem Timeout, einer unterbrochenen Verbindung oder einem 502/503 erneut. Aus Sicht des Clients ist Wiederholen die sichere Wahl: „Ich bin mir nicht sicher, ob es funktioniert hat, also versuche ich es noch einmal."

In der Praxis sieht eine „duplicate create“ so aus:

  • Zwei Bestellungen für einen Checkout
  • Zwei Support-Tickets für eine Beschwerde
  • Zwei Abonnements oder Belastungen für einen Klick
  • Zwei Einladungen an dieselbe Person

Das Beängstigende ist: Der Server kann oft nicht unterscheiden, ob es sich um eine komplett neue Anfrage handelt oder um dieselbe Anfrage, die wiederholt wurde. Wenn Ihr POST-Endpunkt immer eine neue Zeile einfügt, wird jeder Retry zur zweiten Zeile. Der Client zeigt das vielleicht nicht an, aber Ihre Datenbank und Ihre Kunden merken es.

AI-generierte Backends übersehen das besonders leicht, weil sie oft nur den Happy Path modellieren: ein Klick, eine Anfrage, eine Antwort. Timeouts, Doppeltipps oder konkurrierende Anfragen werden häufig nicht berücksichtigt.

Das Ziel ist einfach: Wenn dieselbe Create-Anfrage wiederholt wird, soll sie dasselbe Ergebnis liefern, nicht eine zweite Aktion. Genau davor schützt API-Idempotenz.

API-Idempotenz in einfachen Worten

API-Idempotenz bedeutet: Wenn dieselbe Anfrage mehrmals gesendet wird, ist das Ergebnis dasselbe, als wäre sie einmal gesendet worden.

Das ist wichtig, weil viele Systeme effektiv mit „at-least-once“-Zustellung arbeiten. Eine Anfrage kann einmal oder zweimal ankommen, meistens aber kommt sie an.

Einige Aktionen sind von Natur aus idempotent. Ein typisches GET kann wiederholt werden, ohne etwas zu ändern. Eine Seite neu laden sollte keinen neuen Nutzer anlegen.

Andere Aktionen sind nicht idempotent. Ein typisches POST, das etwas erstellt (eine Bestellung, einen Nutzer, eine Zahlung), kann zweimal laufen und zwei Datensätze oder zwei Abbuchungen erzeugen. Gelingt das erste POST, aber die Antwort geht verloren, wiederholt der Client die Anfrage und erzeugt versehentlich ein Duplikat.

Idempotenz gibt Ihnen einen sicheren Weg zu retrieden. Sie verhindert nicht jede Art von Fehler und behebt keine kaputte Geschäftslogik, aber sie verhindert doppelte Aktionen bei Retries.

Wo Idempotenz am wichtigsten ist

Sie brauchen Idempotenz nicht überall. Sie brauchen sie dort, wo ein Retry eine zweite reale Aktion auslösen kann: mehr Geld einziehen, mehr E-Mails versenden, mehr Jobs starten, mehr Zeilen anlegen.

Priorisieren Sie Endpunkte, die etwas erzeugen oder triggern, das schwer rückgängig zu machen ist. Häufige Beispiele:

  • Zahlungen (charge, authorize, capture)
  • Bestellungen, Abonnements, Buchungen
  • E-Mail-/SMS-Versand (Passwort-Reset, Einladungen, Quittungen)
  • Hintergrund-Jobs (Exporte, Verarbeitung, Berichtserstellung)
  • Webhook-Handler, die etwas anlegen oder gewähren

Achten Sie auch auf Endpunkte, die wie „Updates“ aussehen, aber wie Anhänge funktionieren. Alles, was Zähler erhöht, Items anhängt, Gutschriften anwendet oder Totale ändert, kann bei Retries doppelt angewendet werden.

Idempotency-Keys (Request-IDs): das Grundmuster

Idempotency-Keys sind der praktischste Weg, Schreib-Anfragen retry-sicher zu machen.

Die Idee ist simpel: Der Client schickt mit einer Schreib-Anfrage (meist POST) eine eindeutige Request-ID, und der Server verspricht, dass das Wiederholen derselben Request-ID keine zweite Ressource erzeugt oder die Aktion wiederholt.

Gängig ist ein Header wie Idempotency-Key (oder ein requestId-Feld im Body). Wenn der Client ein Timeout hat und erneut sendet, verwendet er denselben Key.

Auf dem Server speichern Sie pro Key einen kleinen Datensatz. Die meisten Teams legen fest:

  • den Key
  • wem er gehört (Scope)
  • auf welche Operation er sich bezieht (Endpoint, Methode)
  • Status (in_progress, succeeded, failed)
  • die zurückgegebene Antwort (Statuscode und Body)

Scope ist wichtig

Ein Key sollte nicht global im ganzen System gelten. Gute Defaults scoping sind: pro Nutzer, pro Account/Tenant oder pro API-Token und in der Regel pro Endpoint.

Zum Beispiel sollte ein Key, der für POST /orders genutzt wurde, nicht für POST /refunds akzeptiert werden, selbst wenn die Zeichenfolge übereinstimmt.

Aufbewahrung ist ein Kompromiss

Bewahren Sie Keys lange genug auf, um reale Retries abzudecken (Minuten bis Stunden), plus Puffer für langsame Clients und Queues. Manche Teams behalten sie 24 Stunden oder ein paar Tage, um versehentliche Replays abzufangen. Längere Aufbewahrung reduziert Duplikate, erhöht aber Speicherbedarf und zwingt zu Cleanup-Planung.

Replay-Verhalten

Wenn derselbe Key erneut gesendet wird, sollte der Server das ursprüngliche Ergebnis zurückgeben, nicht die Aktion erneut ausführen.

Beispiel: Ein Checkout liefert 201 mit order_id=123. Wenn der Client mit demselben Key erneut sendet, geben Sie dasselbe 201 und dieselbe order_id zurück.

Unique-Constraints: das Sicherheitsnetz für Rennen

Sichern Sie Ihren Prototypen
Wir beheben geleakte Secrets, schwache Auth und Injection-Risiken, die oft mit AI-Code ausgeliefert werden.

Idempotency-Keys helfen, aber sie allein reichen nicht aus. Ihre letzte Verteidigungslinie ist die Datenbank.

Wenn zwei Anfragen Ihren API zur nahezu gleichen Zeit erreichen, kann nur die Datenbank zuverlässig verhindern, dass beide dasselbe erstellen.

Ein Unique-Constraint sagt der Datenbank: „Es darf nur eine Zeile dieses Typs geben.“ Selbst wenn Ihr Code zwei parallele Kopien der Anfrage ausführt, lehnt die DB das Duplikat ab.

Häufige Beispiele:

  • Eindeutige E-Mail in einer users-Tabelle
  • Eindeutiges Paar wie (account_id, external_id) beim Import von Stripe, QuickBooks oder einem CRM
  • Eindeutige order_number

Ein praktisches Muster ist, den Request-ID auf dem erstellten Datensatz zu speichern. Zum Beispiel eine Spalte idempotency_key auf orders hinzufügen und diese eindeutig machen, oft scoped wie (account_id, idempotency_key). Dann führt jeder Retry auf dieselbe Zeile.

Wenn der Constraint greift, macht Ihre API meist eines von zwei Dingen:

  • Die bestehende Ressource zurückgeben (am besten für „create order“, „start checkout“, „create invoice“)
  • Einen klaren Conflict-Fehler zurückgeben (besser, wenn das Wiederverwenden eines Keys einen echten Fehler verschleiern könnte)

Verlassen Sie sich nicht auf „prüfen dann einfügen“ im Anwendungscode. Zwei Worker können beide prüfen „existiert es?“ und beide sehen „nein“, bevor einer einfügt. Machen Sie Einzigartigkeit zur Datenbank-Regel.

Wie man Idempotenz zu einem POST-Endpunkt hinzufügt (Schritt für Schritt)

Wenn ein Client ein Timeout hat und ein POST erneut sendet, wollen Sie, dass der zweite Aufruf dasselbe Ergebnis zurückgibt, nicht eine zweite Zeile erstellt.

Beginnen Sie mit zwei Entscheidungen:

  • Welche Aktionen verursachen echten Schaden, wenn sie wiederholt werden (Bestellungen, Belastungen, Einladungen, Jobs)
  • Wofür der Key scoped ist (pro Endpoint + Nutzer/Account ist ein solider Default)

Implementieren Sie es so, dass es auch unter Konkurrenz korrekt bleibt.

1) Eine stabile Request-ID verlangen

Für riskante Endpunkte verlangen Sie einen Idempotency-Key-Header (oder ein Request-ID-Feld). Clients müssen denselben Wert für Retries wiederverwenden.

2) Den Key persistieren

Entweder legen Sie eine Idempotency-Tabelle an (key, endpoint, user/account, status, response), oder Sie speichern den Key direkt auf dem erstellten Datensatz, wenn eine saubere 1:1-Abbildung besteht.

3) Den Key atomar beanspruchen

Fügen Sie zuerst den Key-Datensatz ein, geschützt durch einen Unique-Constraint (oder nehmen Sie ein Lock). So verhindern Sie, dass zwei gleichzeitige Anfragen beide „gewinnen“.

4) Die zurückgegebene Antwort speichern

Nachdem die Aktion erfolgreich war, speichern Sie die Antwort (Statuscode und Body). Bei Wiederholungen geben Sie diese gespeicherte Antwort zurück, ohne die Arbeit nochmal auszuführen.

5) Entscheiden, was bei laufender Anfrage passiert

Kommt ein Retry, während der erste Versuch noch läuft, brauchen Sie vorhersehbares Verhalten. Gängige Optionen:

  • kurz warten und dann erneut prüfen
  • 409 Conflict (oder 202 Accepted) mit einer klaren "noch in Bearbeitung"-Meldung zurückgeben

Wählen Sie eine Vorgehensweise und halten Sie sie konsistent.

Knifflige Fälle: Timeouts, Konkurrenz und partielle Fehler

Retries werden kompliziert, wenn Client und Server unterschiedliche Vorstellungen davon haben, was passiert ist. Idempotenz macht solche Momente langweilig: gleiche Anfrage, gleiches Ergebnis.

Timeout nach Erfolg

Der Server hat den Datensatz erzeugt, aber die Antwort kam nie an. Ohne Schutz erzeugt der Retry eine zweite Zeile.

Behandeln Sie den Idempotency-Key als Quittung. Wenn der Retry denselben Key nutzt, geben Sie das ursprüngliche Ergebnis zurück.

Absturz mitten im Ablauf und gleichzeitig eintreffende Retries

Das Speichern eines Keys reicht nicht, wenn Sie nicht nachverfolgen, was geschehen ist.

Ein praktisches Regel-Set:

  • Status speichern: pending, succeeded, failed.
  • Sicherstellen, dass nur eine Anfrage den Key besitzen kann (Unique-Constraint ist das einfachste Schutzmittel).
  • Findet ein Retry einen pending-Status, starten Sie keinen zweiten Durchlauf. Warten Sie kurz oder geben Sie eine klare "still processing"-Antwort.
  • Nach Erfolg geben Sie während der Aufbewahrungsdauer immer dieselbe Antwort für denselben Key zurück.

Partielle Fehler

Das ist der schwierigste Fall. Vielleicht erstellen Sie einen Nutzer, schaffen es aber nicht, die Willkommens-E-Mail zu senden, oder Sie buchen eine Zahlung, schaffen es aber nicht, die Bestellzeile zu erstellen.

Wählen Sie eine klare „Quelle der Wahrheit“ für die Anfrage und stellen Sie sicher, dass Retries Nebenwirkungen, die bereits erfolgreich waren, nicht wiederholen. Das bedeutet oft:

  • verbleibende Schritte asynchron fertigstellen
  • einen Kompensationsschritt verwenden (z. B. eine Rückerstattung, wenn die Bestellung nicht erstellt werden kann)

Häufige Fehler, die trotzdem Duplikate zulassen

Kostenloses Code-Audit anfordern
Erhalten Sie eine klare Liste von Problemen und Fixes, bevor Sie sich zu einer größeren Überarbeitung verpflichten.

Die meisten doppelten Erstellungen passieren nicht, weil Idempotency komplett fehlt. Sie passieren, weil Idempotency nur halbherzig hinzugefügt wurde.

Gängige Fehler:

  • Den Idempotency-Key optional machen, sodass nur einige Requests geschützt sind.
  • Den Key speichern, aber das Ergebnis nicht speichern, sodass ein Retry die Arbeit erneut ausführt.
  • Keys nicht richtig scopen (ein Key, der über Nutzer oder Endpunkte hinweg wiederverwendet wird, kann kollidieren).
  • „Prüfen dann einfügen“ ohne DB-Unique-Constraint.
  • Nebenwirkungen wie „E-Mail senden“ als idempotent behandeln, ohne zu verfolgen, ob sie bereits versendet wurden.

Ein klassisches Szenario: POST /orders belastet zuerst die Karte und stürzt dann ab, bevor eine Antwort zurückgegeben wird. Wenn der Retry erneut belastet, haben Sie eine Doppelbelastung. Vermeiden Sie das, indem Sie das Ergebnis persistieren und mit Einzigartigkeit absichern.

Schnell-Checkliste für retry-sicheres Verhalten

Eine retry-sichere API verhält sich bei Wiederholung derselben Aktion immer gleich.

Für Schreib-Endpunkte (POST und manchmal PATCH/DELETE):

  • Für Operationen, die etwas erzeugen oder triggern, Idempotency-Key verlangen.
  • Einen Datenbank-Unique-Constraint für die Dedupe-Regel erzwingen.
  • Prüfen, dass Retries dieselbe Ressourcen-ID und denselben Response-Body (oder eine stabile Repräsentation) zurückgeben.
  • Key-Scope definieren (pro Nutzer/Account + pro Endpoint ist ein guter Ausgangspunkt).
  • Keys lange genug aufbewahren, um reale Retries abzudecken, und ausreichend Kontext loggen, um zu debuggen.

Für Webhook-Handler:

  • Nutzen Sie die Event-ID des Providers als Idempotency-Key, speichern Sie sie und geben Sie bei Wiederholungen Erfolg zurück.

Ein einfacher Test: Senden Sie dieselbe Anfrage fünfmal schnell hintereinander (inklusive desselben Keys). Sie sollten eine Erstellung und vier „gleiches Ergebnis“-Antworten sehen.

Beispiel: Doppelbelastungen in einem instabilen Checkout verhindern

Machen Sie Ihre API retry-sicher
Machen Sie Ihre fehleranfälligen POST-Routen retry-sicher mit Idempotency-Keys und DB-Constraints.

Ein Solo-Gründer testet einen Checkout, der als AI-generierter Prototyp begann. In Demos funktioniert er, im Echtbetrieb ist das Netzwerk instabil und die UI hängt manchmal am Spinner.

Ein Kunde drückt einmal auf „Pay“. Es sieht so aus, als würde nichts passieren. Er tippt wieder. Beide Anfragen erreichen die API.

Ohne Idempotenz behandelt das Backend diese als zwei separate Käufe. Sie können zwei erfolgreiche Zahlungen, zwei Bestellzeilen und ein Support-Ticket erhalten, das mit „Ich habe nur einmal geklickt“ beginnt. Der Kunde kann sogar einen Disput eröffnen, weil er nicht weiß, welche Belastung gültig ist.

Mit einem Idempotency-Key plus DB-Unique-Constraint erstellt die zweite Anfrage nichts Neues. Der Server erkennt den Retry und gibt dasselbe Ergebnis wie beim ersten Aufruf zurück: die ursprüngliche Order-ID und den Zahlungsstatus.

Üblicher Ablauf:

  • Der Client erzeugt einen Idempotency-Key, wenn der Nutzer auf „Pay“ drückt.
  • POST /checkout speichert diesen Key mit dem Bestellversuch.
  • Die Datenbank erzwingt Einzigartigkeit auf (user_id, idempotency_key) oder (merchant_id, idempotency_key).
  • Beim Retry holt die API den vorhandenen Datensatz und gibt ihn zurück.

Support wird einfacher, wenn Sie ein paar Felder loggen: Idempotency-Key, resultierende Order-ID, Payment-Provider-Charge-ID, Zeitstempel und ob eine Anfrage ein Replay war.

Nächste Schritte für AI-generierte APIs, die unter Retries zusammenbrechen

AI-generierte Backends sehen in Demos oft gut aus, brechen aber, wenn Nutzer die Seite neu laden, mobile Netze abbrechen oder Provider Webhooks erneut schicken. Stabilisieren Sie die Endpunkte, die echten Schaden anrichten können, wenn sie zweimal laufen.

Wählen Sie Ihre drei wichtigsten riskanten Operationen (oft Zahlungen, Bestellungen, Einladungen und eingehende Webhooks). Fügen Sie zwei Ebenen hinzu:

  • Idempotency-Keys, um Retries bewusst sicher zu machen
  • DB-Unique-Constraints, um Rennen abzufangen

Führen Sie dann einen kleinen, realistischen Test durch: Doppelklicken Sie den Submit-Button, simulieren Sie ein Client-Timeout und Retry mit derselben Request-ID, und schicken Sie zwei gleichzeitige Requests mit demselben Payload. Sie wollen eine echte Erstellung und konsistente „gleiches Ergebnis“-Antworten.

Wenn Sie ein AI-generiertes Codebase geerbt haben und Duplikate bereits auftreten, ist oft eine gezielte Nachbesserung schneller als ein kompletter Rewrite: Beheben Sie einen Endpunkt Ende-zu-Ende, dann den nächsten. Wenn Sie eine zweite Meinung möchten, FixMyMess (fixmymess.ai) ist auf Diagnose und Reparatur AI-generierter Backends spezialisiert, inklusive Hinzufügen von Idempotency, Uniqueness-Safeguards und produktionsreifem Retry-Verhalten.

Häufige Fragen

Warum sehe ich doppelte Datensätze, obwohl Nutzer versichern, einmal geklickt zu haben?

Weil Netzwerke und Apps erneut versuchen. Eine Anfrage kann auf dem Server erfolgreich verarbeitet worden sein, aber die Antwort geht verloren, oder ein Nutzer tippt doppelt, während die UI hängt. Wenn Ihr POST bei jeder Anfrage eine neue Zeile einfügt, kann jeder Retry zu einem zweiten Eintrag werden.

Was bedeutet “API idempotency” in einfachen Worten?

Idempotenz bedeutet, dass eine wiederholte gleiche Anfrage dasselbe Ergebnis liefert wie einmaliges Senden. Bei Erstell-Aktionen heißt das in der Regel: Bei Retry wird dieselbe Ressource zurückgegeben, statt eine zweite zu erzeugen.

Welche Endpunkte sollte ich zuerst idempotent machen?

Nutzen Sie es dort, wo ein Retry reale Nebenwirkungen haben kann, die Sie nicht zweimal möchten: Geldabbuchungen, Bestellungen, Einladungen oder das Starten langer Jobs. Für Lesezugriffe brauchen Sie es meist nicht; für harmlose Updates oft auch nicht.

Was ist ein Idempotency-Key und wie wird er verwendet?

Ein Idempotency-Key ist eine vom Client generierte Request-ID, die mit einer Schreib-Anfrage gesendet wird, meist im Header Idempotency-Key. Der Server speichert den Key und gibt bei erneutem Auftreten dasselbe Ergebnis zurück, statt die Aktion erneut auszuführen.

Wie sollte ich Idempotency-Keys scope’n, damit sie nicht kollidieren?

Sichern Sie den Scope auf Akteur und Operation. Standardmäßig pro Account oder Benutzer plus Endpoint/Methodik — so kann dieselbe Zeichenkette nicht versehentlich für einen anderen Nutzer oder eine andere Aktion (z. B. Refunds) gelten.

Wie lange sollte ich Idempotency-Keys speichern?

Bewahren Sie sie lange genug auf, um reale Retries und verzögerte Replays abzudecken — typischerweise Stunden bis zu einem Tag. Längere Aufbewahrung reduziert Duplikate, erhöht aber Speicherbedarf und notwendige Cleanup-Prozesse.

Was sollte der Server zurückgeben, wenn derselbe Idempotency-Key erneut gesendet wird?

Geben Sie bei erneutem Empfang dieselbe Antwort zurück — gleichen Statuscode und denselben Body — und wiederholen Sie die Nebenwirkung nicht. Das macht Client-Retries nach Timeouts oder verlorenen Antworten sicher und vorhersagbar.

Warum brauche ich eine Datenbank-Unique-Constraint, wenn ich Idempotency-Keys habe?

Behandeln Sie die Datenbank-Unique-Constraint als letzte Schutzinstanz gegen Race-Conditions. Zwei Prozesse können gleichzeitig “prüfen dann einfügen” und beide ein ‘Nein’ sehen; die DB kann jedoch erzwingen, dass nur eine Zeile existiert, sodass Sie bei Konflikt die vorhandene Zeile lesen und zurückgeben können.

Was passiert, wenn ein Retry eintrifft, während die erste Anfrage noch läuft?

Sie brauchen eine klare Regel für „pending“-Keys, damit die Aktion nicht zweimal ausgeführt wird. Gängige Ansätze: kurz warten und erneut prüfen, oder eine eindeutige "still processing"-Antwort zurückgeben. Wichtig ist, dass am Ende das gespeicherte Ergebnis konsistent ist, das spätere Retries erhalten.

Wie kann ich schnell testen, ob meine API retry-sicher ist?

Senden Sie dieselbe Anfrage mehrfach mit demselben Idempotency-Key und prüfen Sie, ob nur einmal erstellt wird und die Wiederholungen konsistent dasselbe Ergebnis zurückliefern. Wenn Sie ein AI-generiertes Backend geerbt haben, das bei Retries versagt, ist gezielte Behebung oft schneller als ein kompletter Rewrite; FixMyMess (fixmymess.ai) prüft risikoreiche Endpunkte und ergänzt Idempotency sowie DB-Constraints durchgängig.