How to access the raw body of a Stripe webhook request in NestJS
Tags: nestjs, stripe, node
Published: 2021-02-20 22:09:22

"No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?" This error drove me nuts recently, this is my solution.

---

For a current project I'm implementing <a href="https://stripe.com" target="_blank" rel="noopener noreferrer">Stripe</a> with a NestJS API. One powerful feature of Stripe are webhooks. To make them secure, each webhook request from Stripe contains a signature in the `stripe-signature` header. This way it is possible to verify that the webhook request really originates from Stripe. See the <a href="https://stripe.com/docs/webhooks/signatures#verify-official-libraries" target="_blank" rel="noopener noreferrer">Stripe docs</a> for reference.

In Node for example to verify the webhook payload you would use the `stripe.webhooks.constructEvent(request.body, signature, endpointSecret)` method by passing the raw request body, the mentioned signature header and the endpoint secret obtained in from the Stripe dashboard. And this is where the trouble begins...

## NestJS and the raw body

NestJS uses body-parser by default which - you might have guessed it - results in a JSON parsed body. However Stripe's `constructEvent()` method expects the raw request body, which (unfortunately) <a href="https://github.com/expressjs/express/issues/897" target="_blank" rel="noopener noreferrer">hasn't been around in the request object of Express for ages</a> (for good reasons). As NestJS uses Express under the hood by default, you won't find the raw request body there either.

## Let's add the raw request body then

After trying (and failing) to convert the JSON body to something Stripe might accept as a raw body, I stumbled upon this <a href="https://stackoverflow.com/questions/54346465/access-raw-body-of-stripe-webhook-in-nest-js" target="_blank" rel="noopener noreferrer">Stack Overflow question</a> and implemented a solution based on a modified and extended version of the currently <a href="https://stackoverflow.com/a/54788734" target="_blank" rel="noopener noreferrer">accepted answer</a> by MikingTheViking.

First I had to disable body-parser globally for the whole NestJS application in `main.ts`.

```javascript
// main.ts

const app = await NestFactory.create(AppModule, {
  bodyParser: false,
});
```

Then (also in `main.ts`) I re-enabled body-parser as a middleware with the `verify` option set (see the <a href="https://github.com/expressjs/body-parser#verify" target="_blank" rel="noopener noreferrer">body-parser docs</a>). This allows me to add the raw request body to the request object as a string. As I only want to make `rawBody` available if really needed, I check for the presence of the `stripe-siganture` header first and just return if not set.

```javascript
// main.ts

import * as bodyParser from 'body-parser';

...

const rawBodyBuffer = (req, res, buffer, encoding) => {
  if (!req.headers['stripe-signature']) { return; }

  if (buffer && buffer.length) {
    req.rawBody = buffer.toString(encoding || 'utf8');
  }
};

app.use(bodyParser.urlencoded({ verify: rawBodyBuffer, extended: true }));
app.use(bodyParser.json({ verify: rawBodyBuffer }));
```

## The extra mile (optional but nice)

While this works fine and you can access `req.rawBody` in your constructor with the help of Nest's `@Req()` decorator, wouldn't it be nice to use something like `@RawBody()` similar to `@Body()` instead?

It would.

Without further ado, here comes your custom `@RawBody()` decorator:

```javascript
// raw-body.decorator.ts

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const RawBody = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();

    return request.rawBody || null;
  },
);

```

I hope this solution might help someone else too, it is working great for me so far.