Tất cả bài viết
Máy chủ NestJS bật rawBody xác minh chữ ký webhook từ req.rawBody qua guard, nest start expose qua tunnel cục bộ.
NestJSNode.jswebhook debugginglocal testing

Webhook NestJS: rawBody, signature guard và bẫy ValidationPipe

Handler webhook NestJS hỏng một cách tinh vi. ValidationPipe toàn cục loại field trước guard và JSON parse mặc định phá byte provider đã ký. Test cục bộ cần chứng minh rawBody: true, signature guard và validation theo route trước khi dán URL tunnel vào Stripe hoặc GitHub.

Bật raw body lúc bootstrap

Truyền rawBody: true khi tạo app Nest để Express giữ buffer nguyên vẹn:

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

Không có flag này, req.rawBody undefined và mọi HMAC đều fail.

@Body() vs req.rawBody cho chữ ký

Đừng xác minh chữ ký bằng @Body() — Nest đã parse object. Đọc buffer từ 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 chỉ sau khi guard xác nhận. Thứ tự: guard trước, deserialize sau.

Signature guard trước hiệu ứng ValidationPipe

Gói xác minh provider trong guard đọc header và so sánh 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);
  }
}

Áp guard bằng @UseGuards chỉ trên route webhook.

Tránh bẫy ValidationPipe toàn cục

ValidationPipe toàn cục với transform: true có thể ép kiểu trước controller. Route webhook cần rawBody + JSON.parse thủ công sau xác minh.

Test cục bộ nest start + PortPreview

  1. Chạy: npm run start:dev hoặc nest start --watch cổng 3000.
  2. Expose: npx portpreview 3000.
  3. Đăng ký URL tunnel + path webhook.
  4. Kích hoạt giao hàng thử.
  5. Phát lại event ID trùng — xem mẫu retry.

Bẫy thường gặp

ValidationPipe trước chữ ký

Nếu validation chạy trước, Nest có thể biến đổi body và để rawBody rỗng. Guard phải chạy trên raw buffer.

Thứ tự middleware JSON toàn cục

Express json() trước raw-body tiêu thụ stream. Giữ rawBody: true khi tạo factory.

Handler chậm và retry

Trả 200 nhanh sau xác minh, xếp hàng việc nặng, dedupe theo event ID.

Đọc thêm

Đọc thêm về cơ bản về localhost tunnelinggỡ lỗi webhook cục bộ. Cơ chế chữ ký xem hướng dẫn xác minh chữ ký. Handler chống trùng lặp xem mẫu retry và idempotency. bắt đầu PortPreview miễn phí.

Câu hỏi thường gặp

Tại sao req.rawBody undefined trong webhook NestJS?
Truyền rawBody: true cho NestFactory.create. Không có thì Express không gắn raw buffer.
Có thể xác minh chữ ký bằng @Body() trong NestJS?
Không. @Body() trả object đã parse, không phải byte đã ký. Dùng req.rawBody với rawBody: true.
Làm sao test webhook NestJS cục bộ?
Chạy nest start cổng 3000, expose bằng npx portpreview, đăng ký URL tunnel và kích hoạt sự kiện thử.