Los handlers webhook de NestJS fallan de forma sutil. Un ValidationPipe global elimina campos antes del guard y el parsing JSON por defecto destruye los bytes firmados. Las pruebas locales deben demostrar rawBody: true, guards de firma y validación acotada antes de pegar una URL de túnel en Stripe o GitHub.
Habilitar cuerpo crudo en el bootstrap
Pasa rawBody: true al crear la app Nest para que Express conserve un buffer intacto:
async function bootstrap() {
const app = await NestFactory.create(AppModule, { rawBody: true });
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.listen(3000);
}
Sin este flag, req.rawBody es undefined y cada HMAC falla aunque la red sea correcta.
@Body() vs req.rawBody para firmas
Nunca verifiques firmas contra @Body() — Nest ya parseó el objeto. Lee el buffer del 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 };
}
Parsea JSON solo tras confirmar el guard. Orden: guard primero, deserializar después.
Guard de firma antes de efectos de ValidationPipe
Encapsula la verificación del proveedor en un guard que lee cabeceras y compara digests:
@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);
}
}
Aplica el guard con @UseGuards solo en la ruta webhook.
Evitar la trampa del ValidationPipe global
Un ValidationPipe global con transform: true puede coercionar tipos antes del controlador. Las rutas webhook necesitan rawBody + JSON.parse manual tras verificación.
Pruebas locales con nest start + PortPreview
- Inicia:
npm run start:devonest start --watchen puerto 3000. - Expón:
npx portpreview 3000. - Registra URL del túnel + ruta webhook.
- Dispara entregas de prueba.
- Repite event IDs duplicados — ver patrones de reintentos.
Trampas comunes
ValidationPipe antes de la firma
Si la validación corre primero, Nest puede transformar el body y dejar rawBody vacío. Los guards deben ejecutarse sobre el buffer crudo.
Orden del middleware JSON global
Express json() antes de raw-body consume el stream. Mantén rawBody: true al crear la factory.
Handlers lentos y reintentos
Devuelve 200 rápido tras verificar, encola trabajo pesado y deduplica por event ID.
Para profundizar
Para los fundamentos, lea los fundamentos del tunneling localhost y depuración práctica de webhooks en local. Para la criptografía, consulte la guía de verificación de firmas. Para handlers sin duplicados, lea patrones de reintentos e idempotencia. empiece PortPreview gratis.
