Handlers webhook NestJS falham de forma subtil. Um ValidationPipe global remove campos antes do guard e o parsing JSON por defeito destrói os bytes assinados. Testes locais devem provar rawBody: true, guards de assinatura e validação por rota antes de colar URL de túnel no Stripe ou GitHub.
Ativar corpo bruto no bootstrap
Passe rawBody: true ao criar a app Nest para o Express manter um buffer intacto:
async function bootstrap() {
const app = await NestFactory.create(AppModule, { rawBody: true });
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.listen(3000);
}
Sem esta flag, req.rawBody é undefined e cada HMAC falha.
@Body() vs req.rawBody para assinaturas
Nunca verifique assinaturas com @Body() — o Nest já parseou o objeto. Leia o buffer do 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 };
}
Parse JSON só após o guard confirmar. Ordem: guard primeiro, deserializar depois.
Guard de assinatura antes dos efeitos do ValidationPipe
Encapsule a verificação do provedor num guard que lê cabeçalhos e 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);
}
}
Aplique o guard com @UseGuards apenas na rota webhook.
Evitar a armadilha do ValidationPipe global
Um ValidationPipe global com transform: true pode forçar tipos antes do controller. Rotas webhook precisam de rawBody + JSON.parse manual após verificação.
Teste local nest start + PortPreview
- Inicie:
npm run start:devounest start --watchna porta 3000. - Exponha:
npx portpreview 3000. - Registe URL do túnel + path webhook.
- Dispare entregas de teste.
- Repita event IDs duplicados — veja padrões de retry.
Armadilhas comuns
ValidationPipe antes da assinatura
Se a validação correr primeiro, o Nest pode transformar o body e deixar rawBody vazio. Guards devem correr no buffer bruto.
Ordem do middleware JSON global
Express json() antes de raw-body consome o stream. Mantenha rawBody: true na criação da factory.
Handlers lentos e retries
Devolva 200 rápido após verificação, enfileire trabalho pesado e deduplique por event ID.
Para aprofundar
Para fundamentos, leia noções de tunneling localhost e depuração local de webhooks. Para mecânica de assinatura, veja o guia de verificação de assinatura. Para handlers sem duplicatas, leia padrões de retry e idempotência. comece PortPreview grátis.
