Validiere die Daten am Rand der API und zentralisiere die gesamte Fehlerbehandlung in einer einzigen Middleware mit vier Parametern.
Diese Lektion in Kodokon öffnenAlle eingehenden Daten sind feindlich, bis das Gegenteil bewiesen ist: JSON-Rumpf, URL-Parameter, Header. Die Validierung gehört an den Rand der Anwendung - vor den Controller - damit die inneren Schichten auf garantierten Daten arbeiten. Unterscheide anschließend zwei Familien von Fehlern: operative Fehler (ungültige Eingabe, fehlende Ressource, Konflikt), die erwartet werden und in 4xx übersetzt werden, und Programmierfehler (Bugs), die auf Client-Seite ein undurchsichtiges 500 erzeugen müssen und auf Server-Seite einen vollständigen Stack-Trace.
export class HttpError extends Error {
constructor(status, message, details = undefined) {
super(message);
this.status = status;
this.details = details;
}
}Eine Validierungs-Middleware fängt die Anfrage vor dem Controller ab. Die manuelle Version unten zeigt das Prinzip; in der Produktion liefert ein deklaratives Schema (zod ist zum Standard geworden) zusätzlich Typinferenz und strukturierte Meldungen. Der Kompromiss: eine Abhängigkeit und leichte Kosten zur Laufzeit, im Gegenzug für zentralisierte, kombinierbare Regeln, die man unmöglich vergessen kann.
import { HttpError } from './http-error.js';
export function validateUser(req, res, next) {
const { email, name } = req.body ?? {};
const errors = [];
if (typeof email !== 'string' || !email.includes('@')) {
errors.push('email: invalid format');
}
if (typeof name !== 'string' || name.trim().length < 2) {
errors.push('name: 2 characters minimum');
}
if (errors.length > 0) {
return next(new HttpError(400, 'Invalid body', errors));
}
next();
}Express erkennt eine Fehler-Middleware an ihrer Signatur mit vier Parametern (err, req, res, next). Zuletzt registriert, nach allen Routen mit app.use(errorHandler), wird sie zum einzigen Fehlerausgang der API: ein einheitliches Antwortformat, Logging an einer einzigen Stelle, kein Durchsickern interner Informationen. Die Controller ihrerseits rufen einfach next(err) auf.
export function errorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
const status = err.status ?? 500;
const body = { error: err.message };
if (err.details) {
body.details = err.details;
}
if (status >= 500) {
console.error(err);
body.error = 'Internal server error';
}
res.status(status).json(body);
}