adminjs-nestjs: Using AdminBro alongside NestJS breaks payload validation

When the AdminModule is registered into root AppModule the validation errors are triggered in every other place, all the time as If the request contained no payload. I created a minimal reproduction repository https://github.com/pbn4/adminbro-nestjs-validation-bug/blob/master/src/app.module.ts and here is an example request:

curl -X POST http://localhost:3000/ -d '{"hello": "hello"}' -H "Content-Type: application/json"

Comment out AdminModule import in AppModule and see it working again.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 9
  • Comments: 18 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@SimonB407 this issue occurs because admin-bro-nestjs pulls out jsonParser and urlencoded parser from their original position in the stack and pushes it back at the end of the stack. Unfortunately, this may be after all the route handling in nestjs. I’ve wrapped around the admin module to fix this temporarily.

import { AdminModule } from '@admin-bro/nestjs'
import { Module, OnApplicationBootstrap } from '@nestjs/common'
import { HttpAdapterHost } from '@nestjs/core'

import { adminPanelConfig } from './config'

/**
 * Wraps AdminBro AdminModule and reorders express middleware
 * See: https://github.com/SoftwareBrothers/admin-bro-nestjs/blob/e73598c65b5109d0086a566b3aa48ff475117929/src/loaders/express.loader.ts#L29
 * admin-bro uses express formidable which conflicts with NestJs's body parser
 * So admin-bro-nestjs removes body parser from the express middleware stack,
 * adds the admin middleware and then pushes back body parser. But the body parser
 * needs to come before all the other routes in the application. So in this module
 * we pick up admin and body parser middleware and put them before all the other
 * routes
 */
@Module({
  imports: [AdminModule.createAdminAsync(adminPanelConfig)],
})
export class AdminPanelModule implements OnApplicationBootstrap {
  constructor(private readonly _httpAdapterHost: HttpAdapterHost) {}

  onApplicationBootstrap() {
    const { httpAdapter } = this._httpAdapterHost
    const app = httpAdapter.getInstance()

    const jsonParserIndex = app._router.stack.findIndex(
      (layer: { name: string }) => layer.name === 'jsonParser',
    )
    const jsonParser = app._router.stack.splice(jsonParserIndex, 1)

    const urlencodedParserIndex = app._router.stack.findIndex(
      (layer: { name: string }) => layer.name === 'urlencodedParser',
    )
    const urlencodedParser = app._router.stack.splice(urlencodedParserIndex, 1)

    const adminIndex = app._router.stack.findIndex((layer: { regexp: RegExp }) =>
      layer.regexp.toString().includes('admin'),
    )
    const admin = app._router.stack.splice(adminIndex, 1)

    // if admin-bro-nestjs didn't reorder the middleware
    // the body parser would have come after corsMiddleware
    const corsIndex = app._router.stack.findIndex(
      (layer: { name: string }) => layer.name === 'corsMiddleware',
    )

    app._router.stack.splice(
      corsIndex + 1,
      0,
      ...admin,
      ...jsonParser,
      ...urlencodedParser,
    )
  }
}

@Hrach2003 I recommend using 1.1.0-beta.3. Beta 1 had some bugs.

Same happening to me here, all the request came with an empty body.

Any news on the proper fix for this? I wasn’t able to use it in a normal way with the latest published package (1.1.0-beta.3).