บทความทั้งหมด
เซิร์ฟเวอร์ NestJS ที่เปิด rawBody ตรวจลายเซ็น webhook จาก req.rawBody ผ่าน guard และ nest start ผ่าน tunnel ในเครื่อง
NestJSNode.jswebhook debugginglocal testing

NestJS webhook: rawBody, signature guard และกับดัก ValidationPipe

handler webhook NestJS ล้มเหลวแบบละเอียด ValidationPipe ทั่วโลกตัด field ก่อน guard และ JSON parse เริ่มต้นทำลาย byte ที่ provider เซ็น ทดสอบในเครื่องต้องพิสูจน์ rawBody: true, signature guard และ validation เฉพาะ route ก่อนวาง URL tunnel ใน Stripe หรือ GitHub

เปิด raw body ตอน bootstrap

ส่ง rawBody: true ตอนสร้างแอป Nest เพื่อให้ Express เก็บ buffer ที่ไม่ถูกแตะ:

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

ไม่มี flag นี้ req.rawBody เป็น undefined และ HMAC ล้มทุกครั้ง

@Body() vs req.rawBody สำหรับลายเซ็น

อย่าตรวจลายเซ็นด้วย @Body() — Nest parse object แล้ว อ่าน buffer จาก 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 หลัง guard ยืนยัน ลำดับ: guard ก่อน deserialize ทีหลัง

signature guard ก่อน ValidationPipe

ห่อการตรวจ provider ใน guard ที่อ่าน header และเปรียบเทียบ 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);
  }
}

ใช้ guard ด้วย @UseGuards เฉพาะ route webhook

หลีกเลี่ยงกับดัก ValidationPipe ทั่วโลก

ValidationPipe ทั่วโลกที่มี transform: true อาจบังคับ type ก่อน controller route webhook ต้องใช้ rawBody + JSON.parse หลังตรวจ

ทดสอบในเครื่อง nest start + PortPreview

  1. เริ่ม: npm run start:dev หรือ nest start --watch พอร์ต 3000
  2. เปิด: npx portpreview 3000
  3. ลงทะเบียน URL tunnel + path webhook
  4. ทริกเกอร์การส่งทดสอบ
  5. เล่น event ID ซ้ำ — ดู รูปแบบ retry

กับดักที่พบบ่อย

ValidationPipe ก่อนลายเซ็น

ถ้า validation มาก่อน Nest อาจ transform body และทำให้ rawBody ว่าง guard ต้องทำงานบน raw buffer

ลำดับ middleware JSON ทั่วโลก

Express json() ก่อน raw-body กิน stream คง rawBody: true ตอนสร้าง factory

handler ช้าและ retry

คืน 200 เร็วหลังตรวจ คิวงานหนัก dedupe ด้วย event ID

อ่านเพิ่ม

พื้นฐานอ่านที่ พื้นฐาน localhost tunneling และ การดีบัก webhook ในเครื่อง. กลไกลายเซ็นอ่านที่ คู่มือตรวจสอบลายเซ็น. handler ป้องกันซ้ำอ่านที่ รูปแบบ retry และ idempotency. เริ่มใช้ PortPreview ฟรี.

คำถามที่พบบ่อย

ทำไม req.rawBody เป็น undefined ใน NestJS webhook?
ส่ง rawBody: true ให้ NestFactory.create หากไม่มี Express จะไม่แนบ raw buffer
ตรวจลายเซ็นด้วย @Body() ใน NestJS ได้ไหม?
ไม่ได้ @Body() คืน object ที่ parse แล้ว ไม่ใช่ byte ที่เซ็น ใช้ req.rawBody กับ rawBody: true
ทดสอบ NestJS webhook ในเครื่องอย่างไร?
รัน nest start ที่ 3000 เปิดด้วย npx portpreview ลงทะเบียน URL tunnel และทริกเกอร์ event ทดสอบ