server-core
@mmm/server-core
Gemeinsame NestJS-Core-Bausteine fuer Services:
- globales Exception-Handling
- Correlation-ID pro Request
- AsyncLocalStorage-basierter Request-Kontext
- einfache strukturierte JSON-Logs
- Request-Logging-Middleware
Installation
pnpm add @mmm/server-corePeer Dependencies muessen im Service vorhanden sein:
@nestjs/common@nestjs/corereflect-metadatarxjs
Inhalt
Das Paket exportiert:
ServerCoreModuleRequestContextCorrelationIdMiddlewareRequestLoggingMiddlewareAllExceptionsFilterLog
Schnellstart
Modul registrieren
import { Module } from "@nestjs/common";import { ServerCoreModule } from "@mmm/server-core";
@Module({ imports: [ ServerCoreModule.forRoot({ serviceName: "users-service" }) ]})export class AppModule {}ServerCoreModule registriert den globalen AllExceptionsFilter.
Middleware einbinden
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";import { CorrelationIdMiddleware, RequestLoggingMiddleware, ServerCoreModule} from "@mmm/server-core";
@Module({ imports: [ ServerCoreModule.forRoot({ serviceName: "users-service" }) ]})export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(CorrelationIdMiddleware, RequestLoggingMiddleware) .forRoutes("*"); }}Empfohlene Reihenfolge:
CorrelationIdMiddlewareRequestLoggingMiddleware
So steht die Correlation-ID waehrend des gesamten Requests im Kontext zur Verfuegung.
RequestContext
RequestContext basiert auf AsyncLocalStorage und haelt request-lokale Daten:
type RequestContextState = { correlationId?: string; tenantId?: string; userId?: string; permissions?: string[];};Beispiel:
import { RequestContext } from "@mmm/server-core";
const { correlationId, tenantId, userId } = RequestContext.get();
RequestContext.set({ tenantId: "tenant-a", userId: "42", permissions: ["users.read"]});Wichtig: Der Kontext ist nur innerhalb eines laufenden Requests verfgbar, der ueber CorrelationIdMiddleware oder RequestContext.run(...) initialisiert wurde.
CorrelationIdMiddleware
Verhalten:
- liest
x-correlation-idaus dem Request, falls vorhanden - erzeugt sonst eine neue UUID
- schreibt die ID in den
RequestContext - setzt
x-correlation-idauf der Response
Damit koennen Logs und Fehlerantworten ueber eine gemeinsame ID korreliert werden.
RequestLoggingMiddleware
Schreibt nach Abschluss eines Requests einen strukturierten Logeintrag, unter anderem mit:
- HTTP-Methode
- Pfad
- Statuscode
- Dauer in Millisekunden
- User-Agent
AllExceptionsFilter
Der globale Filter:
- mappt unbekannte Fehler auf
500 Internal Server Error - loggt HTTP- und Laufzeitfehler strukturiert
- gibt eine einheitliche JSON-Fehlerantwort zurueck
Beispielantwort:
{ "statusCode": 500, "message": "Internal Server Error", "path": "/users/42", "correlationId": "5dd3de0b-b0fc-4482-b560-1ca8b35c8f65"}Logging
Log schreibt JSON nach stdout bzw. stderr.
import { Log } from "@mmm/server-core";
Log.info("user.created", { userId: "42" });Log.warn("permission.missing", { permission: "users.write" });Log.error("user.create.failed", { reason: "db unavailable" });Unterstuetzte Log-Level:
debuginfowarnerror
Steuerung ueber Umgebungsvariablen:
LOG_LEVEL: Standard istinfoSERVICE_NAME: wird in jeden Logeintrag geschrieben
Beispiel:
LOG_LEVEL=debugSERVICE_NAME=users-serviceBeispiel-Log:
{ "ts": "2026-04-10T10:00:00.000Z", "level": "info", "service": "users-service", "correlationId": "5dd3de0b-b0fc-4482-b560-1ca8b35c8f65", "msg": "http.request", "method": "GET", "path": "/health", "statusCode": 200, "durationMs": 3}Entwicklung
Build:
pnpm buildPublish passiert ueber die Bitbucket-Pipeline.
Hinweise
serviceNamewird aktuell anServerCoreModule.forRoot(...)uebergeben und als Provider registriert.- Fuer den eigentlichen Logeintrag liest
Logderzeitprocess.env.SERVICE_NAME. - Wenn
serviceNamedirekt aus dem Modul im Logger verwendet werden soll, muss der Logger entsprechend erweitert werden.