How to parse the raw body of a request in a nest js controller

Published on June 20, 2021

I recently needed to parse the raw body in a nestJS application to validate a webhook from a third party service. The key to this was using the json validate method to read the raw body in to a variable before passing the request on to the next middleware.

Why would you need raw body?

If you accept any webhooks from third party services you might need to parse the request and verify it with a hash. You can’t modify the request in any way or the hashes will not match the expected value. You must use the request stream as it is sent.

Nest js automatically parses your application requests into JSON. This is perfect for 99% of use cases with NestJS web applications. We have to bypass this functionality.

The steps

  1. Turn off NestJS automatic body parsing
  2. Add required middleware classes
  3. Wire up the middleware in NestJS bootstrapping

Turn off NestJS automatic body parsing

Somewhere in your application you will bootstrap your application

async function bootstrap() {
  const app = await NestFactory.create(AppModule)
  await app.listen(3000)
}

bootstrap()

You need to pass the option to create method that disables body parsing

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { bodyParser: false })
  await app.listen(3000)
}

bootstrap()

Add the required middleware classes

Add a RawBodyMiddleware.ts

import { Injectable, NestMiddleware } from '@nestjs/common'
import { json } from 'body-parser'

/**
 * Copied this middleware to parse the raw response into a param to use later
 * from https://github.com/golevelup/nestjs/blob/master/packages/webhooks/src/webhooks.middleware.ts
 */
@Injectable()
export class RawBodyMiddleware implements NestMiddleware {
  public constructor() {}

  public use(req: Request, res: Response<any>, next: () => any): any {
    json({
      verify: (req: any, res, buffer) => {
        if (Buffer.isBuffer(buffer)) {
          const rawBody = Buffer.from(buffer)
          req['parsedRawBody'] = rawBody
        }
        return true
      },
    })(req, res as any, next)
  }
}

Add a JsonBodyMiddleware

import { Injectable, NestMiddleware } from '@nestjs/common'
import { json } from 'body-parser'

@Injectable()
export class JsonBodyMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => any) {
    json()(req as any, res as any, next)
  }
}

Wire up the middleware into the NestJs Bootstrapping

We need to tell NestJS to parse the specified routes with the raw body parser. Then parse every other route with the regular JSON parser.

Then you can add any middleware you like after that.

// This is an array of routes we want raw body parsing to be available on
const rawBodyParsingRoutes: Array<RouteInfo> = [
  {
    path: '*my/rawbody/required/route*',
    method: RequestMethod.POST,
  },
]

export class AppModule implements NestModule {
  public configure(consumer: MiddlewareConsumer): MiddlewareConsumer | void {
    consumer
      .apply(RawBodyMiddleware)
      .forRoutes(...rawBodyParsingRoutes)
      .apply(JsonBodyMiddleware)
      .exclude(...rawBodyParsingRoutes)
      .forRoutes('*')
      .apply(MyOtherMiddleware)
      .forRoutes({ path: '*', method: RequestMethod.ALL })
  }
}

Access the raw body in a NestJS controller

Now in the controller for handling the specified route we can access the parsedRawBody parameter.

const payload = request.parsedRawBody?.toString()

Tip: If you don’t see a body here make sure that the path you specified is actually triggering the raw body parser.

Darragh ORiordan

Hi! I'm Darragh ORiordan.

I live and work in Sydney, Australia building and supporting happy teams that create high quality software for the web.

I also make tools for busy developers! Do you have a new M1 Mac to setup? Have you ever spent a week getting your dev environment just right?

My Universal DevShell tooling will save you 30+ hours of configuring your Windows or Mac dev environment with all the best, modern shell and dev tools.

Get DevShell here: ✨ https://usemiller.dev/dev-shell


Read more articles like this one...

List of article summaries

#nestjs

A Nest JS Pipeline Cheatsheet

I’m always checking the NestJS documentation to read what a Middleware Vs Interceptor is and I could never remember the order that the different NestJS pipeline elements are called in.

So I made this cheatsheet for myself so I had something to refer to.

#nestjs

Using a dynamic DTO property in a NestJS API

NestJS is designed around using strictly typed properties on models but sometimes it’s useful (and fast!) to allow dynamic types on properties and just store some business domain data as a dynamic serialized blob.

This is the serialized LOB method recommended by Martin Fowler (https://martinfowler.com/eaaCatalog/serializedLOB.html).

Here is how you can have a LOB in a NestJS REST Api with type safety and support for OpenAPI definitions.

#nestjs

Automatically setting empty arrays instead of undefined on typeorm entities

If you have an array on an entity model in typeorm you will have to handle the response when there are no items available to put in the array.

In my application I return the entity model directly for GET requests and I like having arrays as empty instead of undefined properties.

By default typeorm will not set the property to [] so it will be undefined.

Here is how to set an array property to have an empty array instead of undefined.

#nestjs

Fixing validation error in NestJS when using forbidUnknwonValues

If you get unexplained validation errors (http status 400) when calling a NestJS api you might want to check your Query() parameters. Parsing and validating these the wrong way can cause issues.

Comments