Startseite / Artikel / 12 Faustregeln für zuverlässige Node.js-APIs

12 Faustregeln für zuverlässige Node.js-APIs

Praktische Regeln für die Zuverlässigkeit von Node.js-APIs: Zeitlimits, Idempotenz, Transaktionen, Cache-Invalidierung, Protokollierung sowie Latenz bei Abhängigkeiten – vor der Seite um 3 Uhr morgens.

906 Wörter

Manche Lektionen zur Zuverlässigkeit bleiben nur nach einem Ausfall im Gedächtnis haften. Die zwölf unten aufgeführten treten häufig genug in verschiedenen Node.js-Diensten auf, sodass es kostengünstiger ist, sie klar zu formulieren, anstatt sie unter Zeitdruck erneut herauszufinden. Behandeln Sie sie als Standard bei der Gestaltung von Handlern – nicht als optionalen Aufheller nach dem Start.

1. Abrufbare Anfragen werden erneut versucht.

Nicht „könnte“. Clients gehen in die Zeitüberschreitung, Verbindungen werden mitten in der Antwort unterbrochen, und Nutzer drücken ständig auf Senden. Load Balancer sowie Mobilnetze verursachen zusätzlich eigene doppelten Übertragungen. Jeder Handler, der Zustände erstellt oder verändert, benötigt eine klare Antwort auf die Frage „Was passiert, wenn derselbe Datensatz erneut ankommt?“, denn über einen ausreichend langen Zeitraum ist das kein Randfall – es ist unvermeidlich. Entwerfen Sie sowohl den normalen Ablauf als auch den Fall mit doppelten Anfragen gemeinsam.

2. Geben Sie jeder Ausgangsanfrage eine Zeitüberschreitung vor.

Datenbankabfragen, externe HTTP-Anfragen, Zahlungsgateways – wenn man einen Fristtermin weglässt, kann ein blockierter Partner den Socket unbegrenzt offen lassen. Ein einfaches fetch(url) ist nicht „einigermaßen geduldig“; es handelt sich um ein unbegrenztes Warten, und solche unbegrenzten Wartezeiten können dazu führen, dass eine langsame Abhängigkeit alles weiter flussaufwärts lahmlegt. Verwenden Sie lieber die Standardeinstellungen von Bibliotheken, die eine Grenze vorgeben, und führen Sie bei Fehlern laute Fehlermeldungen oder Wiederholungsversuche ein, anstatt zu stummen Blockaden.

3. Browserprüfungen dienen der Benutzererfahrung, nicht als Sicherheitsbarriere.

Aufrufer können Ihre SPA überspringen und direkt die API mit beliebigem JSON über curl, Postman oder ein kompromittiertes Skript aufrufen. Wichtige Regeln – korrekt formatierte E-Mails, nicht-negative Preise, erforderliche Felder – müssen auf dem Server mit derselben Strenge durchgesetzt werden wie gegenüber feindlichem Traffic. Clientseitige Validierung hilft nur kooperativen Nutzern, die ohnehin nicht gegen die Vereinbarungen verstoßen würden.

4. Ein Cache ohne Validierungsmechanismus ist ein verzögerter Fehler.

Durch Caching entsteht eine zweite „Wahrheit“, und die Kopien weichen voneinander ab. Wenn Sie einen Cache einsetzen, entscheiden Sie, welches Schreibvorgang, Webhook oder TTL-Ereignis ihn löscht, und dokumentieren Sie diese Entscheidung neben dem Cache-Schlüssel. Auf die Annahme, „er läuft irgendwann ab“, zu vertrauen, bedeutet oft, dass die Fehlbedingungen länger bestehen bleiben, als die Kunden tolerieren können – insbesondere dann, wenn TTL-Werte aus Gründen der „Leistung“ in Stunden angegeben werden.

5. Verbundene Schreibvorgänge benötigen eine Transaktion, nicht Optimismus.

await update1(); await update2(); wirkt atomar, ist es aber nicht. Zwischen den awaits kann ein Absturz, ein Fehler oder eine Ablehnung dazu führen, dass genau ein Update angewendet wird – dadurch entstehen Abweichungen zwischen Bestand und Rechnung. Nur eine echte Datenbanktransaktion oder eine explizite Saga mit kompensierenden Aktionen schließt dieses Risiko zuverlässig ab.

6. Instrument für Vorfälle, die Sie noch nicht gesehen haben.

Sobald etwas kaputtgeht, ist es zu spät, die Protokolzeintragung hinzuzufügen, die diese Situation erklärt hätte. Strukturierte Protokolle, Korrelations IDs, die zwischen Diensten übertragen werden, sowie ausreichend Kontext zur Rekonstruktion einer vor dem Fehler gesendeten Anfrage sind effektiver als die raffiniertesten Nachforschungen nach dem Fehler. Protokolldaten, die Sie sicher aufbewahren können, Ergebnisse sowie Latenzen von Abhängigkeiten, solange das System funktionsfähig ist.

7. Prozessspeicher ist ein Cache – niemals das System zur dauerhaften Speicherung.

Eine Wert, der nur im Node-Prozess vorhanden ist, verschwindet beim Neustart, ist für gleichzeitige Instanzen unsichtbar und war nie dauerhaft gespeichert. Feature Flags, die im RAM gespeichert sind, Zähler oder „temporäre“ Maps teilen dieses Schicksal. Wenn ein Wert einen Deployment, einen Absturz oder Lastverteilung ohne sticky Sessions überstehen muss, sollte er in einer Datenbank oder einem anderen dauerhaften Speicher abgelegt werden – nicht einfach in einem zufällig verfügbaren RAM-Slot.

8. Planen Sie damit, dass jeder Netzwerkaufruf letztendlich fehlschlägt.

Die wichtige Frage lautet nicht „Funktioniert das normalerweise?“, sondern „Was passiert bei den seltenen Fehlern?“ Zeitüberschreitungen, unvollständige Antworten sowie Anstiege von 5xx-Fehlern zählen alle dazu. Wenn die ehrliche Antwort „unklar“ ist, schließen Sie diese Lücke durch sichere Wiederholungsversuche, Circuit Breaker oder herabgestufte Betriebsmodi, bevor die Produktion das Problem entdeckt.

9. Eine Warteschlange verschiebt die Aufgaben – sie löscht sie nicht.

Das Hinzufügen einer Aufgabe in die Warteschlange wirkt wie eine Erledigung. Ein Worker führt später immer noch dieselbe Arbeit aus, mit denselben Fehlerrisiken, nur zu einem anderen Zeitpunkt. Wenn die Worker im Rückstand sind, wächst der Backlog – obwohl die API weiterhin reaktiv bleibt. Das ist zwar besser als das Blockieren von Anfragen, aber keine günstigere Lösung. Planen Sie die Größe der Verarbeiter sowie der Dead-Letter-Pfade genauso sorgfältig wie die Größe der HTTP-Ebene.

10. Benennen Sie alles für Leser in sechs Monaten – nicht nach heutigen Abkürzungen.

data, temp und handleStuff bleiben nur dann korrekt, wenn drei weitere Konzepte ebenfalls als „data“ gelten. Namen, die mit der Geschäftsidee verbunden sind, bleiben auch für Nutzer verständlich, denen kein Kontext vorliegt – einschließlich des zukünftigen Ichs in einem unbekannten Service. Verwenden Sie lieber Domain-Substantive statt mechanischer Verben, die die ersten Implementierungsdetails beschreiben.

11. Die Ersteller werden zweimal aufgerufen.

Unzuverlässige Netzwerke, aggressive Wiederholungsclienten, ungeduldiges Doppeltippen – irgendetwas davon kann Probleme verursachen. POST-Anfragen zur Erstellung benötigen eine feinfühlige Handhabung von Duplikaten, in der Regel über einen Idempotenzschlüssel, der zusammen mit der resultierenden Ressource gespeichert wird; andernfalls führen Doppelabrechnungen und Duplikatenaufträge zu Zeitproblemen. Stellen Sie sicher, dass „bereits erstellt“ das ursprüngliche Ergebnis zurückgibt und nicht einen neuen Nebeneffekt.

12. Die Latenz des Endpunkts hängt von der langsamsten Abhängigkeit ab.

Ein ansprechend gestalteter Handler, der eine nicht indizierte Abfrage oder eine entfernte API ohne Zeitlimit aufruft, ist dennoch langsam. Das Profilieren eigener Codezeilen unter Ignorierung der aufgerufenen Funktionen beantwortet die falsche Frage. Messen Sie p95 und p99 im gesamten Aufrufgraphen, um anschließend die Indizes, das Batching sowie die Zeitlimits dort zu optimieren, wo tatsächlich Zeit verloren geht.

Keiner dieser Ratschläge ist besonders klug – und genau das ist der Punkt: Seiten, an denen man mitten in der Nacht arbeitet, sind selten klug. Meist handelt es sich dabei um eines dieser zwölf Probleme, die unbeachtet bleiben, bis sie nicht mehr dringend erscheinen. Implementieren Sie die notwendigen Schutzmaßnahmen, solange der Service noch klein genug ist, um geändert zu werden.