Wszystkie artykuły
Serwer NestJS z rawBody weryfikuje podpisy webhook z req.rawBody przez guard, nest start wystawiony przez lokalny tunel.
NestJSNode.jswebhook debugginglocal testing

Webhooki NestJS: rawBody, guardy podpisu i pułapki ValidationPipe

Handlery webhook NestJS psują się subtelnie. Globalny ValidationPipe usuwa pola przed guardem, a domyślne parsowanie JSON niszczy podpisane bajty. Test lokalny powinien potwierdzić rawBody: true, guardy podpisu i walidację na trasie przed wklejeniem URL tunelu do Stripe lub GitHub.

Włącz raw body przy bootstrap

Przekaż rawBody: true przy tworzeniu aplikacji Nest, aby Express zachował nietknięty buffer:

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

Bez tej flagi req.rawBody jest undefined i każdy HMAC pada.

@Body() vs req.rawBody dla podpisów

Nigdy nie weryfikuj podpisu przez @Body() — Nest już sparsował obiekt. Czytaj buffer z middleware raw-body:

@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 };
}

Parsuj JSON dopiero po guardzie. Kolejność: guard, potem deserializacja.

Guard podpisu przed efektami ValidationPipe

Enkapsuluj weryfikację providera w guardzie czytającym nagłówki i porównującym digest:

@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);
  }
}

Stosuj guard przez @UseGuards tylko na trasie webhook.

Unikaj pułapki globalnego ValidationPipe

Globalny ValidationPipe z transform: true może wymuszać typy przed kontrolerem. Trasy webhook potrzebują rawBody + ręcznego JSON.parse po weryfikacji.

Test lokalny nest start + PortPreview

  1. Start: npm run start:dev lub nest start --watch na porcie 3000.
  2. Wystaw: npx portpreview 3000.
  3. Zarejestruj URL tunelu + ścieżkę webhook.
  4. Wywołaj testowe dostawy.
  5. Powtórz zduplikowane event ID — zobacz wzorce retry.

Typowe pułapki

ValidationPipe przed podpisem

Jeśli walidacja idzie pierwsza, Nest może przekształcić body i zostawić pusty rawBody. Guardy muszą działać na raw buffer.

Kolejność globalnego middleware JSON

Express json() przed raw-body zużywa stream. Zachowaj rawBody: true przy tworzeniu factory.

Wolne handlery i retry

Szybko zwracaj 200 po weryfikacji, kolejkuj ciężką pracę, deduplikuj po event ID.

Więcej

Podstawy znajdziesz w podstawach tunelowania localhost oraz praktycznym lokalnym debugowaniu webhooków. Mechanika podpisu — w przewodniku weryfikacji podpisu. Obsługa bez duplikatów — w wzorach retry i idempotencji. zacznij PortPreview za darmo.

Najczęściej zadawane pytania

Dlaczego req.rawBody jest undefined w webhooku NestJS?
Przekaż rawBody: true do NestFactory.create. Bez tego Express nie dołącza surowego bufferu.
Czy mogę weryfikować podpis przez @Body()?
Nie. @Body() zwraca sparsowany obiekt, nie podpisane bajty. Użyj req.rawBody z rawBody: true.
Jak testować webhooki NestJS lokalnie?
Uruchom nest start na 3000, wystaw przez npx portpreview, zarejestruj URL tunelu i wywołaj zdarzenia testowe.