DEV Community

loading...

How to parse the raw body of a request in a NestJS controller

Darragh O'Riordan
Hi! I'm Darragh ORiordan. I live and work in Sydney, Australia enjoying the mountains and the ocean. I build and support happy teams that create high quality software for the web!
Originally published at darraghoriordan.com on ・2 min read

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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)
  }
}
Enter fullscreen mode Exit fullscreen mode

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)
  }
}
Enter fullscreen mode Exit fullscreen mode

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 })
  }
}
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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

Discussion (0)