Alle Artikel
Ein NestJS-Server mit rawBody prüft Webhook-Signaturen aus req.rawBody per Guard, nest start über lokalen Tunnel exponiert.
NestJSNode.jswebhook debugginglocal testing

NestJS-Webhooks: rawBody, Signatur-Guards, ValidationPipe-Fallen

NestJS-Webhook-Handler scheitern subtil. Ein globaler ValidationPipe entfernt Felder vor dem Guard, und Standard-JSON-Parsing zerstört die signierten Bytes. Lokales Testen soll rawBody: true, Signatur-Guards und route-spezifische Validierung beweisen, bevor du eine Tunnel-URL bei Stripe oder GitHub einträgst.

Raw Body beim Bootstrap aktivieren

Übergib rawBody: true bei NestFactory.create, damit Express einen unveränderten Buffer behält:

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { rawBody: true });
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
  await app.listen(3000);
}

Ohne dieses Flag ist req.rawBody undefined und jeder HMAC-Check scheitert.

@Body() vs req.rawBody für Signaturen

Signiere niemals gegen @Body() — Nest hat das Objekt bereits geparst. Lies den Buffer vom raw-body-Middleware:

@Post('stripe')
@UseGuards(StripeSignatureGuard)
handleStripe(@Req() req: RawBodyRequest) {
  const payload = req.rawBody;
  const event = JSON.parse(payload.toString('utf8'));
  this.events.process(event);
  return { received: true };
}

Parse JSON erst nach Guard-Bestätigung. Reihenfolge: Guard zuerst, Deserialisierung danach.

Signatur-Guard vor ValidationPipe-Effekten

Kapsle Provider-Verifikation in einem Guard mit Header-Lesen und constant-time-Vergleich:

@Injectable()
export class StripeSignatureGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const req = context.switchToHttp().getRequest>();
    const sig = req.headers['stripe-signature'] as string;
    return verify(req.rawBody, sig, process.env.STRIPE_WEBHOOK_SECRET);
  }
}

Wende den Guard mit @UseGuards nur auf der Webhook-Route an.

Die ValidationPipe-Falle vermeiden

Ein globaler ValidationPipe mit transform: true kann Typen vor dem Controller erzwingen. Webhook-Routen brauchen rawBody + manuelles JSON.parse nach Verifikation.

Lokales Testen mit nest start + PortPreview

  1. API starten: npm run start:dev oder nest start --watch auf Port 3000.
  2. Exponieren: npx portpreview 3000.
  3. Tunnel-URL + Webhook-Pfad registrieren.
  4. Test-Lieferungen auslösen.
  5. Doppelte Event-IDs wiederholen — siehe Retry-Muster.

Häufige Stolperfallen

ValidationPipe vor Signaturprüfung

Läuft Validierung zuerst, kann Nest den Body transformieren und rawBody leer lassen. Guards müssen auf dem Raw-Buffer laufen.

Reihenfolge globales JSON-Middleware

Express json() vor raw-body verbraucht den Stream. rawBody: true bei Factory-Erstellung beibehalten.

Langsame Handler und Retries

Nach Verifikation schnell 200 zurückgeben, schwere Arbeit queuen, per Event-ID deduplizieren.

Weiterführend

Grundlagen finden Sie unter localhost-Tunneling-Grundlagen und praktisches lokales Webhook-Debugging. Zur Signaturmechanik siehe den Leitfaden zur Signaturprüfung. Für duplikatsichere Handler lesen Sie Retry- und Idempotenz-Muster. starten Sie PortPreview kostenlos.

Häufig gestellte Fragen

Warum ist req.rawBody in meinem NestJS-Webhook undefined?
Übergib rawBody: true an NestFactory.create. Ohne das hängt Express keinen Raw-Buffer an.
Kann ich Signaturen mit @Body() prüfen?
Nein. @Body() liefert ein geparstes Objekt, nicht die signierten Bytes. Nutze req.rawBody mit rawBody: true.
Wie teste ich NestJS-Webhooks lokal?
nest start auf 3000, mit npx portpreview exponieren, Tunnel-URL registrieren und Test-Events auslösen.