Zum Hauptinhalt
Zurück zu Projekten
E-Commerce-API logo

E-Commerce-API

Backend-Dienst

Architekturdiagramm der E-Commerce-API E-Ticaret API Arayüzü olmayan bir servis — sözleşme, kimlik ve gözlemlenebilirlik Express + Prisma · Docker ile paketlenir Swagger dokümanı kodun yanında üretilir İstemci web · mobil · üçüncü taraf tek sözleşme, tek doküman Ara katman zinciri helmet → cors → rate limit → JWT doğrulama → rol kontrolü → Zod şema doğrulama Hata gövdesi tek biçimde; log alanları fast-redact ile maskelenir, token ve parola log'a girmez. 15 ROTA GRUBU auth product category producer tag cart order coupon address wishlist review webhook auditLog health /metrics Prisma · PostgreSQL 18 model · 12 migration ürün, varyant görseli, kategori, üretici, etiket, kullanıcı, sipariş, kupon, yorum, denetim kaydı Kimlik: JWT + refresh + OAuth kısa ömürlü erişim token'ı, ayrı tabloda refresh token arctic ile sağlayıcı akışı · OAuthAccount modeli parolalar bcrypt ile saklanır Gözlemlenebilirlik: winston ile yapılandırılmış log, morgan ile istek kaydı, prom-client ile metrik ucu. Arayüzü olmayan bir serviste "çalışıyor mu" sorusunun tek cevabı bunlar.
Ein Dienst ohne Oberfläche auf Express und Prisma. Jede Anfrage durchläuft dieselbe Middleware-Kette: helmet, cors, Rate Limit, JWT-Prüfung, Rollenprüfung und Zod-Schemavalidierung. Fünfzehn Routengruppen bilden achtzehn Prisma-Modelle ab; die Identität beruht auf JWT, Refresh-Token und OAuth, die Beobachtbarkeit auf winston, morgan und prom-client. Der Fehlerkörper ist einheitlich, Log-Felder werden maskiert.

Ein E-Commerce-Backend ohne Oberfläche: 15 Routengruppen, 18 Prisma-Modelle, Identität über JWT und Refresh-Token, Anbieter-Login, Rate-Limiting, ein Prüfprotokoll und ein Metrik-Endpunkt. Der Vertrag entsteht aus Swagger, der Dienst wird mit Docker paketiert.

Verwendete Technologien

  • Node.js
  • Express
  • TypeScript
  • Prisma
  • PostgreSQL
  • Zod
  • JWT
  • Swagger
  • Docker

Hauptmerkmale

  • 15 Routengruppen — Katalog, Warenkorb, Bestellungen, Gutscheine, Bewertungen, Wunschliste, Webhooks, Prüfprotokoll
  • JWT-Access-Token + Refresh-Token in eigener Tabelle + Anbieter-Login
  • Der Bestellstatus ist in einer eigenen Statusmaschine definiert, nicht in den Controllern
  • Schutz vor wiederholten Anfragen — dieselbe Anfrage wird nicht zweimal verarbeitet
  • Strukturierte Logs, Anfrage-IDs und Maskierung; Token und Passwörter gelangen nie ins Log
  • Ein Prometheus-kompatibler Metrik-Endpunkt hinter Authentifizierung

Entwicklungszeit

2 Monate

Problem

In einer Anwendung mit Oberfläche ist der Bildschirm die Antwort auf "läuft es". In einem Dienst ohne Oberfläche gibt es diese Antwort nicht: Der Dienst kann laufen und sich falsch verhalten, und nur die konsumierende Seite bemerkt es — meist zu spät. Das eigentliche Problem ist hier also nicht die Fachlogik, sondern Vertrag und Sichtbarkeit. Zweitens ist in einer E-Commerce-API eine wiederholte Anfrage der Normalfall: Die Verbindung bricht ab, der Client versucht es erneut, der Zahlungsanbieter schickt dieselbe Benachrichtigung ein zweites Mal. Wird "was passiert bei doppelter Verarbeitung" an jedem Endpunkt einzeln beantwortet, wird irgendwann einer vergessen.

Nutzer

Zwei Arten von Konsumenten. Der direkte Client — eine Web- oder mobile Oberfläche: liest den Katalog, schreibt Warenkörbe und Bestellungen, verwaltet Sitzungen. Der serverseitige Konsument — ein Zahlungs- oder Versandanbieter: schickt Webhooks, weist sich mit einem geteilten Geheimnis aus und kann dieselbe Benachrichtigung wiederholen. Es gibt eine dritte Partei: die betreibende Person, deren Bedarf kein Bildschirm ist, sondern Logs, Metriken und ein Prüfprotokoll.

Meine Rolle

Einzelner Entwickler. Datenmodell und Migrationen, die Middleware-Kette, Identitäts- und Berechtigungsschicht, Bestell-Statusmaschine, Prüf- und Beobachtbarkeitsschicht, der Swagger-Vertrag und das Docker-Paket.

Architektur

Ein einzelner Dienst auf Express; eine Anfrage durchläuft eine feste Middleware-Kette: Sicherheits-Header, Ressourcenfreigabe-Richtlinie, Anfrage-ID, Rate-Limiting, JWT-Prüfung, Rollenprüfung, Besitzprüfung und Zod-Schemaprüfung. Am Ende der Kette führt der Controller nur die Fachlogik aus; Fehlerbehandlung und die Form des Fehlerkörpers sind in einer einzigen Fehler-Middleware gebündelt, sodass jeder Endpunkt denselben Fehlerumschlag liefert. Der Bestellstatus liegt nicht in den Controllern, sondern in einem eigenen Statusmaschinen-Modul — welcher Zustand in welchen übergehen darf, steht an einer Stelle. Wiederholte Anfragen behandelt eine eigene Idempotenz-Middleware, sodass kein Endpunkt seine eigene Lösung erfindet. Auf der Datenseite Prisma und PostgreSQL; das Schema führt für die Produktsuche eine eigene Suchvektor-Spalte, und Listen-Endpunkte nutzen cursorbasierte Paginierung statt Seitenzahlen. Die Beobachtbarkeit hat drei Teile: strukturierte Logs, Anfrageprotokollierung und ein Metrik-Endpunkt.

Zentrale Entscheidungen

  1. Idempotenz liegt in der Middleware, nicht in den Endpunkten

    Eine wiederholte Anfrage ist nicht endpunktspezifisch; Hinzufügen zum Warenkorb, Anlegen einer Bestellung und Verarbeiten eines Webhooks teilen dasselbe Problem. Liegt die Lösung in der Middleware, kann kein Endpunkt sie vergessen — der Schutz hängt an der Routendefinition, nicht an der Disziplin eines Controllers.

    KompromissDer Client muss einen Idempotenzschlüssel erzeugen und bei einem erneuten Versuch denselben senden; ein Client, der das nicht tut, profitiert nicht vom Schutz. Der Speicher der verarbeiteten Schlüssel wächst zudem stetig und muss aufgeräumt werden.

  2. Der Bestellstatus ist ein eigenes Modul, nicht Teil der Controller

    Der Bestell-Lebenszyklus ist die am stärksten verzweigte Stelle des Systems: Zahlung, Kommissionierung, Versand, Zustellung, Storno und Rückgabe. Sind die Übergangsregeln über Controller verstreut, wird dieselbe Regel an mehreren Stellen unterschiedlich geschrieben und läuft mit der Zeit auseinander. Sie in einem Modul zu bündeln, macht die gültigen Übergänge an einer Stelle lesbar.

    KompromissDie Regel liegt in der Anwendung, nicht in der Datenbank: Ein direkt in SQL ausgeführtes Update kann die Statusmaschine umgehen. So wie ich bei Ustura die Überschneidungsregel auf eine Datenbank-Constraint reduziert habe, hätten sich auch hier Übergänge auf Datenebene erzwingen lassen; das ist nicht geschehen.

  3. Der Refresh-Token liegt in einer eigenen Tabelle

    Der Access-Token ist kurzlebig und zustandslos; der Refresh-Token existiert als Datensatz in der Datenbank. Der Grund ist die Widerrufbarkeit: Ein zustandsloser Refresh-Token bleibt bis zum Ablauf gültig, und eine gestohlene Sitzung ließe sich nicht schließen. Anbieter-Logins werden über ein eigenes Kontomodell verknüpft, sodass eine Person mehrere Anmeldewege haben kann.

    KompromissJede Erneuerungsanfrage bedeutet einen Datenbanklesevorgang — der Skalierungsvorteil eines zustandslosen Entwurfs wird hier bewusst aufgegeben. Auch diese Tabelle wächst, abgelaufene Datensätze müssen aufgeräumt werden.

  4. Metrik- und Dokumentationsendpunkte blieben nicht offen

    Der Metrik-Endpunkt beschreibt den inneren Zustand des Systems, die Swagger-Oberfläche den gesamten Vertrag. Beides ist Aufklärungsmaterial für einen Angreifer. Beide wurden hinter Authentifizierung gelegt; der Webhook-Endpunkt wird zusätzlich mit einem geteilten Geheimnis geprüft.

    KompromissGeschlossene Dokumentation bedeutet einen zusätzlichen Zugriffsschritt für Entwickler, die die API nutzen wollen. Auch der Schutz des Metrik-Endpunkts erfordert eine eigene Konfiguration auf der Sammler-Seite — mit einem Sammler ohne Authentifizierungsunterstützung funktioniert er nicht direkt.

  5. Log-Felder werden maskiert

    Den Anfragekörper zu loggen, erleichtert die Fehlersuche, doch derselbe Körper kann Passwort, Token oder Adresse enthalten. Maskiert wird beim Schreiben und feldweise; ein sensibles Feld gelangt gar nicht erst ins Log, statt nachträglich bereinigt zu werden.

    KompromissDie Liste der zu maskierenden Felder wird von Hand gepflegt; ein neues sensibles Feld muss eingetragen werden, und nichts erinnert daran. Ein maskiertes Feld ist auch beim Debuggen unsichtbar, manche Fehler lassen sich also nicht über das Log verfolgen.

Technologiewahl

Express wurde gewählt, damit die Middleware-Kette lesbar bleibt: Was eine Anfrage in welcher Reihenfolge durchläuft, lässt sich am Code verfolgen — und das heißt, Sicherheitsentscheidungen bleiben sichtbar. Prisma hält Beziehungen und Migrationshistorie eines Schemas mit 18 Modellen nachvollziehbar; die Schemadatei ist zugleich Dokumentation des Datenmodells. Zod bündelt die Prüfung des Anfragekörpers in einer Middleware und liefert die Typen aus demselben Schema. Der Swagger-Vertrag steht als Kommentar neben dem Code — so läuft ein separates Dokument schwerer vom Code weg. Docker macht den Dienst mitsamt seinen Abhängigkeiten zu einer lauffähigen Einheit; in einem Dienst ohne Oberfläche bedeutet "läuft auf meiner Maschine" nichts.

Sicherheit

Die Middleware-Kette beginnt mit Sicherheits-Headern, die Ressourcenfreigabe-Richtlinie ist ausdrücklich definiert. Rate-Limiting ist je Endpunkt konfiguriert und bei Identitätsendpunkten enger. Passwörter liegen mit bcrypt gehasht vor, der Access-Token ist kurzlebig, und weil der Refresh-Token in der Datenbank steht, ist er widerrufbar. Die Berechtigung hat drei Stufen: Authentifizierung, Rollenprüfung und Besitzprüfung — selbst eine Person mit der richtigen Rolle kann fremde Datensätze nicht bearbeiten. Der Webhook-Endpunkt wird mit einem geteilten Geheimnis geprüft. Datei-Uploads laufen über eine eigene Middleware. Metrik- und Swagger-Endpunkte liegen hinter Authentifizierung. Änderungen werden in ein eigenes Prüfprotokoll-Modell geschrieben; Log-Felder werden beim Schreiben maskiert.

Performance

Dies ist ein Backend-Dienst; eine Seitengeschwindigkeitsmessung ist bedeutungslos, und da keine gespeicherte Lasttest-Ausgabe vorliegt, steht hier keine Zahl. Strukturell gibt es drei Entscheidungen: Listen-Endpunkte nutzen cursorbasierte Paginierung, sodass die Abfragekosten auch bei tiefen Seiten konstant bleiben; für die Produktsuche führt das Schema eine eigene Suchvektor-Spalte, sodass die Suche nicht bei jeder Anfrage Text durchsucht; und weil der Metrik-Endpunkt Werte wie Anfragezahl und -dauer nach außen gibt, lässt sich das Verhalten des Dienstes messen statt vermuten.

Ergebnisse

Routen
15 Routengruppen — Identität, Katalog, Warenkorb, Bestellungen, Gutscheine, Bewertungen, Adressen, Wunschliste, Webhooks, Prüfprotokoll, Health, Metriken
Schema
18 Prisma-Modelle · 11 Migrationen
TypeScript-Quelldateien
99
Middleware
15 Middleware-Module — Identität, Berechtigung, Besitz, Rate-Limiting, Idempotenz, Validierung, Maskierung, Fehler
Schema-Historie
Die Migrationsdaten reichen von 2026-04 bis 2026-06; das Repository trägt einen einzigen Import-Commit
Architekturgarantie
Idempotenz ist in der Middleware definiert; kein Endpunkt kann den Schutz umgehen
Architekturgarantie
Metrik- und Dokumentationsendpunkte sind ohne Authentifizierung nicht erreichbar

Was ich heute anders machen würde

In diesem Repository gibt es keine automatischen Tests, und bei einem Backend-Dienst ist das die teuerste Lücke. Die einfachste und wertvollste Teststelle liegt auf der Hand: Die Bestell-Statusmaschine ist bereits ein reines Modul, als Ein- und Ausgabe prüfbar, und trägt die am stärksten verzweigte Regel des Systems. Heute würde ich dort beginnen und danach die Idempotenz-Middleware mit derselben, zweimal gesendeten Anfrage prüfen. Der zweite Punkt ist der Ort der Zustandsregel: Ich habe die Übergänge in der Anwendung gehalten, während bei Ustura die Reduktion der Überschneidungsregel auf eine Datenbank-Constraint den Schutz auf alle Schreibpfade zugleich anwandte. Hier kann ein direkt in SQL ausgeführtes Update die Statusmaschine umgehen; heute würde ich zumindest eine Check-Constraint gegen ungültige Zustände ergänzen.

Backend-Dienst

Finden wir gemeinsam den richtigen Einstieg

Sagen Sie mir, was Sie bauen möchten; ich sage Ihnen vorab, wie lange es dauert und was der sinnvolle erste Schritt ist.