How to get both parsed body and raw body in Express

By

Learn how to get both the parsed JSON body and the raw body in Express by adding a verify function to express.json that saves the buffer to req.rawBody.

~~~

You can get both the parsed body and the raw body in Express by passing a verify function to express.json(), and saving the raw buffer to req.rawBody there.

In one application I’m building, I had this problem.

Using Express, I can parse the body as JSON with the built-in middleware. It’s been part of Express since 4.16, so there’s no need to install body-parser yourself:

app.use(express.json())

However to integrate with the Stripe payments API I had the need to expose the raw body (not parsed) into an endpoint, and I couldn’t figure out how to do it, while still parsing the body as JSON.

Why do you need the raw body at all?

Stripe signs every webhook it sends you. To verify the signature, you must compute a hash over the exact bytes Stripe sent.

Once express.json() turns those bytes into a JavaScript object, the original bytes are gone. You might think you can rebuild them with JSON.stringify(req.body), but that produces different whitespace and key ordering, so the signature check fails. This is the pitfall almost everyone hits with Stripe webhooks.

The fix is to keep a copy of the raw buffer before parsing happens.

The solution

express.json() accepts a verify option. It’s a function called with the request, the response, and the raw body buffer, before the body is parsed.

We use it to store the buffer on the request object:

app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf
  }
}))

Now the raw body is available on req.rawBody and the JSON parsed data is available on req.body.

In the webhook handler you pass req.rawBody to the signature check, and keep using req.body everywhere else:

app.post('/webhooks/stripe', (req, res) => {
  const signature = req.headers['stripe-signature']
  //verify using req.rawBody and the signature
})

If your app still uses the standalone body-parser package, nothing changes. express.json() is that package under the hood, so it accepts the same verify option:

import bodyParser from 'body-parser'

app.use(bodyParser.json({
  verify: (req, res, buf) => {
    req.rawBody = buf
  }
}))

For new code I’d just use express.json(). I wrote more about the body parsers in how to retrieve POST data in Express.

What’s the cost?

From the body-parser GitHub I found that this doubles the RAM usage for every request, since we keep the raw buffer around next to the parsed object. But since I need this functionality, I have no other way.

If that worries you, an alternative is to apply the verify function only where you need it. You can mount express.raw({ type: 'application/json' }) on the webhook route alone, so req.body is the raw buffer there, and keep plain express.json() for everything else. That way the rest of the app pays no extra memory cost.

Except perhaps creating a different server just for the Stripe webhook I wanted to handle. For my case, the verify trick was enough.

Tagged: Node.js · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about node: