Middleware ist eine Funktion in der Anfrage-Pipeline, die Anfragen und Antworten prüft oder verändert, bevor Ihre Routenlogik läuft. Zwei Bedeutungen teilen sich das Wort – die ältere aus der Unternehmens-IT (Message-Broker und Integrationsbusse zwischen Anwendungen) und die des Web-Frameworks, um die es hier geht: Funktionen innerhalb einer Anwendung, die jede Anfrage der Reihe nach durchläuft. Express formuliert das Modell schlicht: Eine App ist im Kern eine Folge von Middleware-Aufrufen – und die Reihenfolge dieser Aufrufe ist buchstäblich das Programm.
Das Wichtigste im Überblick
| Frage | Antwort |
|---|---|
| Der Vertrag | Prüfen/verändern → dann antworten (Kurzschluss) oder next() aufrufen |
| Die Form | Eine Zwiebel: Anfragen gehen den Stack hinunter, Antworten blubbern zurück nach oben |
| Das Gesetz | Registrierungsreihenfolge = Ausführungsreihenfolge – die meisten Middleware-Bugs sind Reihenfolge-Bugs |
| Der kanonische Stack | Header → CORS → Parsing → Logging → Authn → Authz → Limits → Routen → 404 → Fehler |
| vs. das Gateway | Middleware läuft innerhalb einer App; ein Gateway steht vor vielen |
Der Stack, in der richtigen Reihenfolge
// JavaScript / Node.js — Express + Parse Server
// Middleware: functions the request flows through, in registration order
const app = express();
app.use(helmet()); // 1 · security headers
app.use(cors(corsOptions)); // 2 · CORS before anything that fails
app.use(express.json({ limit: '1mb' })); // 3 · body parsing, bounded
// Parse Server IS middleware — a whole backend mounted into the stack
app.use('/parse', new ParseServer(config).app);
app.use(notFoundHandler); // 404 — after all routes
app.use(errorHandler); // error handler LAST (4 args) // Flutter / Dart — Back4app Flutter SDK
// One SDK call — and the platform's middleware stack ran the gauntlet:
final response =
await QueryBuilder<ParseObject>(ParseObject('Post')).query();
// Before your query touched data, the request passed through:
// security headers → CORS → body limits → key check → session auth
// → rate limiting → routing → (your beforeFind trigger) → the database
// You wrote none of it — that's the middleware a BaaS runs for you. // iOS / Swift — Back4app Swift SDK
// One SDK call — and the platform's middleware stack ran the gauntlet:
let posts = try await Post.query().find()
// Before your query touched data, the request passed through:
// security headers → CORS → body limits → key check → session auth
// → rate limiting → routing → (your beforeFind trigger) → the database
// You wrote none of it — that's the middleware a BaaS runs for you. // Android / Kotlin — Back4app Android SDK
// One SDK call — and the platform's middleware stack ran the gauntlet:
val posts = ParseQuery.getQuery<ParseObject>("Post").find()
// Before your query touched data, the request passed through:
// security headers → CORS → body limits → key check → session auth
// → rate limiting → routing → (your beforeFind trigger) → the database
// You wrote none of it — that's the middleware a BaaS runs for you. Jede Position hat einen Grund: Security-Header zuerst (sie müssen auf jeder Antwort stehen, auch auf Fehlern); CORS vor allem, was scheitern kann (sonst verschleiern Browser den echten Fehler); Body-Parsing begrenzt und vor den Routen (sonst ist req.body undefiniert); Authentifizierung vor Autorisierung (Berechtigungen, die gegen niemanden geprüft werden, sind Berechtigungen für jeden); Rate-Limiting vor teurer Arbeit (ein Limiter nach der Datenbankabfrage schützt nichts); die 404 nach allen Routen; der Fehler-Handler ganz zuletzt.
Die Zwiebel, richtig gezeichnet
Die Hälfte, die die meisten Erklärungen auslassen: Die Pipeline läuft in beide Richtungen. Die Django-Dokumentation zeichnet sie als Zwiebel – jede Middleware eine Schicht um die View im Kern – und Code nach dem next()-Aufruf (oder nach get_response) läuft auf dem Rückweg der Antwort, in umgekehrter Reihenfolge. Dort wird die Antwortzeit gemessen, dort werden Header gesetzt, und dort protokolliert das Logging, was tatsächlich passiert ist. Eine Schicht, die kurzschließt, überspringt nicht nur den Handler; sie überspringt beide Hälften jeder inneren Schicht – und genau das ist die Garantie, für die ein Auth-Tor existiert.
Reihenfolge-Bugs, die in Produktion gehen
Der allgemeine Rat lautet “die Reihenfolge zählt”; die konkreten Bugs sind lehrreicher. Autorisierung vor Authentifizierung: Die Berechtigungsprüfung läuft gegen einen anonymen Akteur – sporadische 401er und 403er, nirgends eine Exception, stundenlange Fehlersuche. Body-Parser nach den Routen: Jeder Handler sieht req.body === undefined und gibt dem Client die Schuld. Auth vor CORS: Der Browser blockiert die 401-Antwort selbst, weil ihr die CORS-Header fehlen, sodass das Frontend einen Netzwerkfehler statt des echten sieht. Statische Dateien vor der Auth: private Dateien, munter an Unauthentifizierte ausgeliefert. Fehler-Handler nicht zuletzt: Fehler, die nach seiner Position im Stack geworfen werden, erreichen ihn nie. Jeder einzelne davon besteht auf dem glücklichen Pfad jeden Rauchtest – Reihenfolge-Bugs sind die Sorte, die in Produktion geht.
Kurzschließen: wenn kein next() der Sinn der Sache ist
Der Vertrag hat zwei erlaubte Ausgänge: die Kontrolle weitergeben oder den Zyklus beenden. Ihn früh zu beenden ist kein Versagen der Middleware – es ist die Hälfte ihrer Aufgabe: die 401 vom Auth-Tor, die 429 vom Limiter, der Cache-Treffer, die Weiterleitung, die an Ort und Stelle beantwortete CORS-Preflight-Anfrage. Die Regel, die beide Ausgänge ehrlich hält: Tun Sie immer genau eines – antworten oder next(). Keines von beidem lässt die Anfrage ewig hängen; beides wirft “headers already sent”-Fehler, die alle weiter unten verwirren.
Dieselbe Idee in jedem Framework
| Framework | Die Middleware ist | Kontrolle weitergeben | Auf dem Rückweg |
|---|---|---|---|
| Express | (req, res, next) => {} | next() | Code nach next() (mit Bedacht) |
| Django | Callable, das get_response umschließt | get_response(request) | Code nach dem Aufruf – die Zwiebel |
| Rack / Rails | Objekt mit call(env) | @app.call(env) | Nachdem der Aufruf zurückkehrt |
| Koa / Hono | async (ctx, next) => {} | await next() | Nach dem await – erstklassig |
Ein Modell, vier Dialekte. Der Fehlerpfad bekommt pro Framework seine eigene Konvention – die Signatur mit vier Argumenten (err, req, res, next) in Express ist der Registrierungsmechanismus, weshalb das Löschen eines “ungenutzten” Parameters den Fehler-Handler stillschweigend in eine normale Middleware verwandelt, die nie feuert.
Middleware vs. Gateways vs. Hooks
| Middleware | API-Gateway | Daten-Hooks | |
|---|---|---|---|
| Läuft | Im Prozess einer App | Vor vielen Apps | Um Datenoperationen herum |
| Granularität | Pro Anfrage | Pro Anfrage, dienstübergreifend | Pro Save/Delete/Find |
| Besitzt | Die Pipeline dieser App | Routing, Auth am Rand, globale Limits | Validierung, Datenreaktionen |
| Konfiguriert über | Code, der Reihe nach | Infrastrukturkonfiguration | Registrierung pro Klasse |
Drei Abfangschichten, eine Schachtelung: Das Gateway steht vor der Flotte, Middleware führt den Parcours jeder App aus, und Hooks feuern dort, wo Anfragen zu Daten werden. Ein Anliegen gehört in die äußerste Schicht, die darüber entscheiden kann – globale Rate-Limits ins Gateway, Session-Authentifizierung in die Middleware, “ist dieser Schreibvorgang gültig” in den Hook.
Typische Anwendungsfälle
- Authentifizierung und Session-Behandlung – die Identität einmal, früh, für alles Folgende feststellen.
- Querschnittliche Hygiene – CORS, Security-Header, Kompression, Anfrage-IDs.
- Eingabedisziplin – Body-Parsing mit Größenbegrenzung, Durchsetzung des Content-Type, Validierung.
- Observability – Logging und Zeitmessung um die gesamte Pipeline gelegt, über den Rückweg der Zwiebel.
- Verkehrsschutz – Rate-Limits und Missbrauchssperren, die kurzschließen, bevor Kosten anfallen.
In welche Schicht gehört das? Entscheidungsmatrix
| Anliegen | Schicht |
|---|---|
| Gilt für jede App, die Sie betreiben | Gateway |
| Gilt für jede Anfrage dieser App | Middleware, bewusst positioniert |
| Gilt für bestimmte Routen | Middleware auf Router-Ebene |
| Gilt, wenn Daten geschrieben oder gelesen werden | beforeSave- / beforeFind-Hooks |
| Eigene Geschäftsoperationen | Funktionen, keine Pipeline-Tricks |
| Fehler in Form bringen | Fehler-Middleware – zuletzt, vier Argumente, keine Ausnahmen |
Grenzen und Trade-offs
- Die Reihenfolge ist unsichtbar, bis sie es nicht mehr ist. Der Stack liest sich von oben nach unten, scheitert aber auf Weisen, die überallhin zeigen; behandeln Sie die Middleware-Registrierung als geprüften, tragenden Code.
- Jede Schicht besteuert jede Anfrage. Zehn Middleware zu je 2 ms sind 20 ms auf jeder Antwort; messen Sie den Stack so, wie Sie Abfragen messen.
- Globaler Zustand ist eine Falle. Middleware läuft nebenläufig über Anfragen hinweg; alles gemeinsam Veränderliche wird zum Wettlauf – hängen Sie anfragebezogene Daten an das Anfrageobjekt, nirgendwo sonst.
- Pipelines verbergen den Kontrollfluss. Eine kurzschließende Schicht drei Ebenen tief kann der Grund sein, warum eine Route “nie läuft”; der Debugging-Griff ist immer derselbe: den Stack ausgeben, der Reihe nach.
- Nicht alles ist ein Pipeline-Anliegen. Geschäftslogik, die in Middleware geschmuggelt wird, koppelt jede Route daran; die Pipeline ist für querschnittliche Anliegen da, nicht für zentrale.
Middleware mit Back4app
Back4app ist eine Open-Source-Plattform für Backend as a Service (BaaS), die eine verwaltete Datenbank, automatisch generierte REST- und GraphQL-APIs, Authentifizierung, Dateispeicher und Serverless-Funktionen mit Cloud Code kombiniert. Der Zusammenhang ist ungewöhnlich wörtlich: Der Server von Back4app ist selbst Express-Middleware – der JavaScript-Tab zeigt ihn mit app.use('/parse', …) in einen Standard-Stack eingehängt – und die Plattform lässt jede Anfrage den kanonischen Parcours durchlaufen: Security-Header, CORS, begrenztes Parsing, Schlüsselprüfungen, Session-Authentifizierung und Rate-Limits, in der richtigen Reihenfolge, als Infrastruktur gepflegt. Ihre eigene Logik pro Anfrage geht dann dorthin, wohin die Entscheidungsmatrix zeigt, statt in handgestrickten Pipeline-Code: beforeSave-/beforeFind-Trigger für datennahe Regeln, Cloud Functions für Operationen – jeweils mit angehängtem Nutzerkontext der Anfrage, was das meiste von dem ist, was eigene Middleware je wissen wollte.
Häufige Fragen
Was ist Middleware, einfach gesagt?
Eine Funktion, die auf dem Weg zwischen einer eingehenden Anfrage und Ihrer Routenlogik sitzt und jede Anfrage im Vorbeigehen verarbeitet – wie die Sicherheitskontrollen eines Flughafens vor dem Gate. Jede prüft oder verändert die Anfrage und reicht sie dann weiter oder stoppt sie auf der Stelle.
Was sind typische Beispiele für Middleware?
Der übliche Stack: Security-Header, CORS, Body-Parsing mit Größenbegrenzung, Logging, Authentifizierung, Autorisierung, Rate-Limiting, Ausliefern statischer Dateien und – ganz am Ende – die 404- und Fehler-Handler. Fast alles Querschnittliche in einer Webanwendung ist Middleware.
Wie funktioniert die Middleware-Kette?
Jede Funktion beendet entweder den Zyklus, indem sie eine Antwort sendet, oder ruft next() auf, um die Kontrolle an die nächste weiterzugeben; das Framework läuft den Stack in Registrierungsreihenfolge ab, bis etwas antwortet. Der klassische Bug: weder antworten noch next() aufrufen – die Anfrage hängt für immer.
Kommt es auf die Reihenfolge der Middleware an?
Sie ist die Fehlerquelle Nummer eins. Autorisierung vor Authentifizierung prüft Berechtigungen gegen niemanden; ein Body-Parser nach den Routen lässt req.body undefiniert; Auth vor CORS lässt Browser den echten Fehler verschleiern; ein Fehler-Handler irgendwo außer am Ende fängt nichts ab. Die Reihenfolge ist das Programm.
Was ist eine Error-Handling-Middleware?
Eine Middleware, an die das Framework Fehler weiterleitet statt an die normale Kette – in Express erkennbar an ihrer Signatur mit vier Argumenten (err, req, res, next) und zuletzt registriert. Geworfene Fehler und next(err)-Aufrufe überspringen alles andere und landen dort, weshalb ihre Position nicht verhandelbar ist.
Was ist der Unterschied zwischen Middleware und einem Routen-Handler?
Absicht und Position. Middleware kümmert sich um Querschnittsthemen vieler Routen und gibt die Kontrolle meist weiter; der Routen-Handler ist das Ziel, das die Antwort erzeugt. In den meisten Frameworks sind es strukturell identische Funktionen – die Pipeline endet nur bei einer davon.
Was ist der Unterschied zwischen Middleware und einem API-Gateway?
Die Reichweite. Middleware läuft im Prozess einer einzigen Anwendung, pro Anfrage; ein Gateway ist Infrastruktur vor vielen Anwendungen und übernimmt Routing, Authentifizierung und Rate-Limits über Dienste hinweg. Ein Gateway ist die Middleware Ihrer gesamten Architektur – beide ergänzen sich, statt zu konkurrieren.
Wann sollte eine Middleware next() NICHT aufrufen?
Wenn sie die Anfrage vollständig erledigt hat: eine Auth-Ablehnung mit 401, ein Rate Limiter mit 429, ein Cache-Treffer, eine Weiterleitung, eine beantwortete CORS-Preflight-Anfrage. Der Kurzschluss ist das Feature – die Garantie, dass hinter dem Tor nichts läuft für Anfragen, die daran gescheitert sind.