Skip to content

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

Terminal window
pnpm add @mmm/server-core

Peer Dependencies muessen im Service vorhanden sein:

  • @nestjs/common
  • @nestjs/core
  • reflect-metadata
  • rxjs

Inhalt

Das Paket exportiert:

  • ServerCoreModule
  • RequestContext
  • CorrelationIdMiddleware
  • RequestLoggingMiddleware
  • AllExceptionsFilter
  • Log

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:

  1. CorrelationIdMiddleware
  2. RequestLoggingMiddleware

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-id aus dem Request, falls vorhanden
  • erzeugt sonst eine neue UUID
  • schreibt die ID in den RequestContext
  • setzt x-correlation-id auf 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:

  • debug
  • info
  • warn
  • error

Steuerung ueber Umgebungsvariablen:

  • LOG_LEVEL: Standard ist info
  • SERVICE_NAME: wird in jeden Logeintrag geschrieben

Beispiel:

Terminal window
LOG_LEVEL=debug
SERVICE_NAME=users-service

Beispiel-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:

Terminal window
pnpm build

Publish passiert ueber die Bitbucket-Pipeline.

Hinweise

  • serviceName wird aktuell an ServerCoreModule.forRoot(...) uebergeben und als Provider registriert.
  • Fuer den eigentlichen Logeintrag liest Log derzeit process.env.SERVICE_NAME.
  • Wenn serviceName direkt aus dem Modul im Logger verwendet werden soll, muss der Logger entsprechend erweitert werden.