Startseite / Artikel

Sechs Integrationsmuster für eine zuverlässige Verbindung von Node.js-Diensten

Erlernen Sie die grundlegenden Muster hinter zuverlässigen Node.js-Integrationen: Request-Response-Verfahren, Polling, Webhooks, API-Schlüssel sowie JWT- und OAuth-Authentifizierung, Wiederholungsversuche mit Backoff-Strategien und Datenkommapping.

Verlag: REACTAPP.TOP7 Min. Lesezeit1511 Wörter

Quellen: 6 API Integration Patterns Every Node.js Integration Engineer Must Know

Es ist einfach, eine HTTP-Anfrage zu senden; die eigentliche Herausforderung besteht darin, eine Integration aufrechtzuerhalten, wenn das andere System langsam ist, down ist oder anders strukturiert ist. Dieser Leitfaden behandelt sechs Muster, die immer wieder auftreten, wenn Node.js-Dienste mit Zahlungsdienstleistern, CRM-Systemen, ERP-Systemen sowie untereinander kommunizieren, sowie die Fallstricke, die eine Demo von einer zuverlässigen Integration unterscheiden.

1. Anfrage-Antwort: die synchrone Grundlage

Im Anfrage-Antwort-Modell sendet der Client eine Anfrage, der Server führt die Arbeit aus und die Antwort kommt über dieselbe Verbindung zurück. Dies umfasst die meisten alltäglichen Operationen: Anmelden, Erstellen oder Aktualisieren von Datensätzen, Abrufen eines Kunden, Löschen einer Ressource.

app.get("/users/:id", async (req, res) => {
  const user = await db.findUser(req.params.id);
  res.json(user);
});

Es ist einfach zu debuggen und die Standardlösung für CRUD-Operationen. Seine Schwäche liegt in zeitaufwändigen Aufgaben: Ein großer Bericht oder eine große Datei kann die Timeout-Werte des Clients oder Proxys überschreiten. Beachten Sie außerdem, dass dieser Handler bei fehlendem Benutzer null mit Status 200 zurückgibt; ein echter Endpunkt sollte stattdessen 404 melden.

2. Polling: Warten, bis die Aufgabe abgeschlossen ist

Wenn ein Ergebnis nicht sofort erzeugt werden kann, kann der Server die Aufgabe entgegennehmen, einen Identifikator zurückgeben und den Client dazu bringen, wieder nachzufragen. Typische Fälle sind das Warten auf die Abwicklung einer Zahlung, das Erstellen eines Berichts, das Transkodieren eines Videos oder den Versand einer Bestellung.

Durch Starten der Aufgabe wird deren ID zurückgegeben:

POST /reports

Response:
{
  "jobId": "abc123"
}

Der Client prüft anschließend in regelmäßigen Abständen den Status-Endpunkt:

GET /reports/abc123/status

bis die Aufgabe mitteilt, dass sie abgeschlossen ist:

{
  "status": "completed"
}

Polling ist einfach und firewall-freundlich, weshalb viele Unternehmenssysteme weiterhin darauf angewiesen sind. Der Nachteil besteht in unnötigem Traffic, da die meisten Überprüfungen mit „noch nicht“ antworten. Streuen Sie die Anfragen aus (beachten Sie ggf. den Wert von Retry-After), legen Sie eine Gesamtfrist fest und handhaben Sie sowohl fehlgeschlagene als auch abgeschlossene Vorgänge.

3. Webhooks: Die andere Seite ruft Sie an

Webhooks ändern die Richtung des Kommunikationsflusses. Anstatt dass Ihr Service immer wieder nachfragt, sendet der Anbieter bei einem Ereignis – wie z. B. erfolgreicher Zahlung, Auftragserteilung, eingehender Nachricht, Kundenanmeldung oder Lageränderung – eine HTTP-Anfrage an Ihren Endpunkt. Stripe, Slack, GitHub und Shopify setzen stark auf dieses Modell.

Ein minimales Empfängerprogramm in Express:

app.post("/webhook/payment", (req, res) => {
  const event = req.body;

  if (event.type === "payment.success") {
    console.log("Payment received!");
  }

  res.sendStatus(200);
});

Das zeigt die Grundstruktur, doch ein Produktiv-Empfänger benötigt mehr Funktionen:

  • Überprüfen Sie den Absender. Jeder kann an eine öffentliche URL senden. Anbieter signieren in der Regel jede Übertragung, und Sie müssen diese Signatur überprüfen (in der Regel im Vergleich zum Rohdateninhalt der Anfrage), bevor Sie dem Ereignis vertrauen.
  • Rechnen Sie mit Duplikaten. Die Übertragungen werden in der Regel so lange wiederholt, bis sie bestätigt werden, sodass dasselbe Ereignis mehrmals eintreffen kann. Speichern Sie die bearbeiteten Ereignis-IDs und stellen Sie sicher, dass die Verarbeitungsfunktionen idempotent sind.
  • Bestätigen Sie schnell. Geben Sie schnell eine 2xx-Antwort zurück und legen Sie langsame Aufgaben in eine Warteschlange; andernfalls kann der Anbieter die Verbindung zeitlich beenden und erneut senden.

Ereignisgesteuerte Integration wird zunehmend bevorzugt, da sie den Polling-Traffic eliminiert.

4. Authentifizierung: Nachweisen, wer anruft

Jede Integration beginnt mit Anmeldeinformationen. Drei Ansätze decken die meisten Fälle ab.

API-Schlüssel

Das einfachste Verfahren sendet ein gemeinsames Geheimnis in einem Header:

GET /users

x-api-key: abc123xyz

In Node.js mit axios sollte der Schlüssel aus der Konfiguration stammen und niemals aus dem Quellcode:

axios.get(url, {
  headers: {
    "x-api-key": process.env.API_KEY
  }
});

API-Schlüssel eignen sich für interne Tools sowie einfache Server-zu-Server-Integrationen. Sie enthalten an sich weder eine Ablaufzeit noch eine Benutzeridentität, daher sollten sie regelmäßig ausgetauscht und so eng wie möglich eingegrenzt werden, wie es der Anbieter zulässt.

JSON Web Tokens

JWTs ermöglichen stateless-Authentifizierung. Die übliche Abfolge:

  1. Der Benutzer melden sich an.
  2. Der Server gibt ein signiertes Token aus.
  3. Der Client speichert das Token.
  4. Jeder nachfolgende Anfrage enthält es in einem Header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Ausgabe eines Tokens in Node.js:

const token = jwt.sign(
  { userId: 1 },
  process.env.JWT_SECRET
);

Jeder Dienst mit der Überprüfungsdatei kann den Token ohne Session-Speicher validieren, was sich gut für Microservices eignet. Das Problem ist die Widerrufbarkeit: Ein Token bleibt bis zu seinem Ablauf gültig, daher sollte man immer expiresIn festlegen – was in diesem Auszug weggelassen wird.

OAuth 2.0

OAuth 2.0 ist es, das den delegierten Zugriff ermöglicht: „Mit Google anmelden“, Facebook-Anmeldung, Microsoft Enterprise Single Sign-On, CRM-Connector sowie alle Drittanbieter-Apps, die im Auftrag eines Benutzers handeln. Im gängigen Authorization-Code-Flow:

  1. Der Benutzer genehmigt den beantragten Zugriff beim Anbieter.
  2. Der Anbieter leitet mit einem kurzfristig gültigen Authorization-Code zurück.
  3. Ihr Server tauscht diesen Code gegen einen Zugriffstoken ein.
  4. API-Aufrufe werden mit dem Zugriffstoken durchgeführt.

Es lohnt sich, diese Themen im Detail zu erlernen: Zugriffs- und Erneuerungstoken, der Authorization Code Flow, der Client Credentials Flow für Machine-to-Machine-Anrufe, PKCE sowie OpenID Connect – eine Identitätsschicht auf Basis von OAuth.

5. Widerstandsfähigkeit: Design für Ausfälle

Netzwerke gehen in die Zeit aus und Abhängigkeiten funktionieren nicht mehr. Zuverlässige Integrationen gehen davon aus, dass dies vorkommen kann.

Wiederholte Anläufe bei fehlgeschlagenen Aufrufen

Ein einfacher Wiederholungszyklus:

for (let i = 0; i < 3; i++) {
  try {
    return await axios.get(url);
  } catch (error) {
    console.log("Retrying...");
  }
}

So wie er implementiert ist, wird sofort wieder versucht, ohne Pause – und wenn alle drei Versuche fehlschlagen, wird anstelle des letzten Fehlers undefined zurückgegeben, sodass der Aufrufer nicht erkennen kann, was schiefgelaufen ist. Werfen Sie den Fehler nach dem letzten Versuch erneut aus und wiederholen Sie nur temporäre Fehler (Netzwerkfehler, Zeitüberschreitungen, 5xx-, 429-Fehler), nicht jedoch 4xx-Fehler, die jedes Mal auf dieselbe Weise fehlschlagen. Seien Sie vorsichtig bei der Wiederholung nicht-idempotenter Anfragen wie Zahlungen, es sei denn, die API unterstützt Idempotenzschlüssel.

Zeitüberschreitungen

Eine Anfrage ohne Zeitüberschreitung kann unbegrenzt hängen und dabei Ressourcen blockieren. Setzen Sie immer eine ein:

axios.get(url, {
  timeout: 5000
});

Exponentielles Zurücksetzen

Wiederholte Anfragen im gleichen Intervall belasten einen schwierig arbeitenden Dienst weiterhin stark:

1s
1s
1s
1s

Das Verdoppeln der Wartezeit bei jeder Anfrage gibt dem Dienst Raum zur Erholung:

1s
2s
4s
8s
16s

Die Berechnung erfolgt in einer einzigen Zeile:

const delay = attempt =>
  Math.pow(2, attempt) * 1000;

Begrenzen Sie die maximale Verzögerung und fügen Sie zufällige Schwankungen hinzu, damit viele gleichzeitig fehlerhafte Clients nicht im Gleichschritt erneut versuchen, eine Anfrage zu senden.

Circuit Breaker

Ein Circuit Breaker zählt die Fehlschläge bei einem downstream Dienst und stoppt nach Überschreiten einer Schwellenwert die Anrufe für eine Abkühlungsphase, bevor er einen erneuten Versuch unternimmt. Dadurch werden kaskadierte Fehlschläge, Ressourcenerschöpfung sowie systemweite Ausfälle verhindert. Opossum ist eine gängige Wahl in Node.js; Resilience4j übernimmt diese Funktion in Java, während Hystrix in älteren Systemen verwendet wird.

6. Datenumwandlung: Übersetzung zwischen Systemen

Zwei Systeme stimmen selten in der Struktur der Daten überein, weshalb ein großer Teil der Integrationsarbeit in der Umwandlung besteht.

Feldzuordnung

Eines der Systeme verwendet camelCase:

{
  "firstName": "John"
}

ein anderes erwartet snake_case:

{
  "first_name": "John"
}

Ein kleiner Mapper verbindet sie miteinander:

function transform(user) {
  return {
    first_name: user.firstName
  };
}

Einfache Mapper-Funktionen lassen sich leicht unit-testen und halten die Besonderheiten der Anbieter außerhalb der Geschäftslogik.

JSON und XML

Viele veraltete Systeme verwenden weiterhin XML, während neuere Dienste JSON erzeugen; daher wandelt eine Integrationsschicht oft in beide Richtungen zwischen diesen Formaten um.

Datumsangaben

Eine Seite kann das ISO-Format senden:

2026-06-26

während die andere Seite eine Tag-zuerst-Notation erwartet:

26/06/2026

Wandeln Sie explizit um und seien Sie bei Datumsangaben mit Zeitangaben klar bezüglich der Zeitzonen – ein Datum, das über Mitternacht verschoben wird, ist ein klassischer Integrationsfehler.

Währungsbeträge

Ein Service gibt einen Dezimalbetrag an:

{
  "amount": 10.50
}

der andere verlangt Ganzzahlabzweige:

{
  "amount_cents": 1050
}

Fließkommazahlen können nicht jeden Dezimalwert exakt darstellen, daher sollte bei der Umrechnung gerundet werden (zum Beispiel Math.round(amount * 100)) und intern lieber mit Ganzzahlcents gearbeitet werden.

Wie sich die Muster in der Praxis kombinieren

In einer typischen Unternehmensarchitektur steht ein Node.js-Gateway vor mehreren Backends:

Frontend
   |
Node.js API Gateway
   |
   +-- Authentication Service
   +-- Payment Service
   +-- CRM Service
   +-- ERP Systems
   +-- Notification Service

Diese Verbindungen kombinieren die oben genannten Muster: Request-Response-APIs, JWT und OAuth, Webhooks, Wiederholungsversuche und Fehlerbehandlungssysteme, Transformationsschichten sowie Überwachung und Protokollierung. Für Ingenieure, die in die Integration arbeiten, sind zunächst REST-APIs, Webhooks, JWT, OAuth 2.0, Datentransformation und ereignisgesteuertes Design von Vorteil, gefolgt von Techniken zur Wiederholung und Resilienz sowie Polling-Verfahren und Circuit Breakers.

Kernpunkte

  • Verwenden Sie Request-Response für schnelle Operationen; wechseln Sie auf Polling oder Webhooks, wenn die Verarbeitung Zeit in Anspruch nimmt.
  • Überprüfen Sie die Signaturen der Webhooks, entfernen Sie doppelte Ereignisse und bestätigen Sie diese umgehend.
  • Speichern Sie Anmeldeinformationen in der Konfiguration, legen Sie Fristen für Tokens fest und lernen Sie die OAuth-Flüsse richtig kennen.
  • Wiederholen Sie Versuche nur bei vorübergehenden Fehlern, wobei Zeitlimits, begrenzte exponentielle Backoff-Strategien sowie Jitter angewendet werden sollten.
  • Isolieren Sie die Datenkommunikation in getesteten reinen Funktionen und gehen Sie bei Datums- sowie Geldwerten besonders sorgfältig vor.