Усі статті
NestJS-сервер з rawBody перевіряє підписи вебхука з req.rawBody через guard, nest start відкритий через локальний тунель.
NestJSNode.jswebhook debugginglocal testing

Вебхуки NestJS: rawBody, signature guards і пастки ValidationPipe

Webhook-обробники NestJS ламаються непомітно. Глобальний ValidationPipe видаляє поля до guard, а стандартний JSON-парсинг знищує підписані байти. Локальне тестування має підтвердити rawBody: true, signature guards і валідацію на маршруті перед вставкою URL тунnelю в 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);
}

Без цього прапора req.rawBody undefined і кожна HMAC-п перевірка падає.

@Body() vs req.rawBody для підписів

Ніколи не перевіряйте підпис через @Body() — Nest уже розпарсив об'єкт. Читайте 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 };
}

Парсіть JSON лише після guard. Порядок: guard, потім десеріалізація.

Signature guard до ефектів ValidationPipe

Інкапсулюйте перевірку провайдера в guard з читанням заголовків і constant-time порівнянням:

@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 лише на webhook-маршруті.

Уникайте пастки глобального ValidationPipe

Глобальний ValidationPipe з transform: true може змінювати типи до контролера. Webhook-маршрути потребують rawBody + ручного JSON.parse після перевірки.

Локальне тестування nest start + PortPreview

  1. Запуск: npm run start:dev або nest start --watch на порту 3000.
  2. Відкрийте: npx portpreview 3000.
  3. Зареєструйте URL тунnelю + шлях webhook.
  4. Надішліть тестові доставки.
  5. Повторіть дублікати event ID — див. патерни повторів.

Типові пастки

ValidationPipe до перевірки підпису

Якщо валідація йде першою, Nest може трансформувати body і залишити rawBody порожнім. Guards мають працювати на raw buffer.

Порядок глобального JSON middleware

Express json() до raw-body споживає stream. Зберігайте rawBody: true при створенні factory.

Повільні handler і повтори

Швидко повертайте 200 після перевірки, ставте важку роботу в чергу, дедуплікуйте за event ID.

Далі

Основи — у основах тунелювання localhost та практичному локальному налагодженні вебхуків. Механіка підписів — у посібнику з перевірки підпису. Обробники без дублікатів — у патернах повторів та ідемпотентності. почніть PortPreview безкоштовно.

Поширені запитання

Чому req.rawBody undefined у webhook NestJS?
Передайте rawBody: true у NestFactory.create. Інакше Express не прикріпить raw buffer.
Чи можна перевіряти підпис через @Body()?
Ні. @Body() повертає розібраний об'єкт, не підписані байти. Використовуйте req.rawBody з rawBody: true.
Як тестувати NestJS вебхуки локально?
Запустіть nest start на 3000, відкрийте через npx portpreview, зареєструйте URL тунnelю та надішліть тестові події.