Nest: @Body() don't transfer body object to dto object

Created on 10 Apr 2018  路  17Comments  路  Source: nestjs/nest

I'm submitting a...


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

@Controller()
export class ApiController {
  @Post()
  async index(@Body() body: BodyDTO) {
     // `body` is not a BodyDTO, just a normal object
     console.log(body instanceof BodyDTO) // false
  }
}

Expected behavior

@Controller()
export class ApiController {
  @Post()
  async index(@Body() body: BodyDTO) {
     console.log(body instanceof BodyDTO) // true
  }
}

What is the motivation / use case for changing the behavior?

Sometimes, I need to do something with dto object.

class BodyDTO {
  readonly firstName: string
  readonly lastName: string
  get name(): string { return this.firstName + ' ' + this.lastName }
}

It's not convenience without dto object. And it's not a intuitive way.

Environment


Nest version: 4.6.6


For Tooling issues:
- Node version: 9.11.1  
- Platform:  Mac 
question 馃檶

Most helpful comment

@unlight Thanks

It works for me.

// ...
async index(@Body(new ValidationPipe({transform: true})) body: BodyDTO) {
  console.log(body instanceof BodyDTO) // true
}
// ...

I suggest updating docs.

All 17 comments

Hi . @XGHeaven, you missing something, you should do this @Body(), if you look the doc, because i need to instantiate it.

@Controller()
export class ApiController {
  @Post()
  async index(@Body() body: BodyDTO) {
     return body;
  }
}

I tried @Body(). It still not works.
And I updated my question with @Body().

@XGHeaven , how you call your ENDPOINT ?

@Controller('api')
export class ApiController {
  @Post()
  async index(@Body() body: BodyDTO) {
     return `Hello ${body.firstName} ${body.lastName}`;
  }
}
//...

class BodyDTO {
  readonly firstName: string
  readonly lastName: string
  get name(): string { return this.firstName + ' ' + this.lastName }
}

It's work fine for me with

$ curl -d '{"firstName":"John", "lastName":"Doe"}' -H "Content-Type: application/json" -X POST http://localhost:3000/api

Can you try this one?

// ...
  return `Hello ${body.firstName} ${body.lastName} ${body instanceof BodyDTO}`
// ...

I get Hello John Doe false. But I want to get Hello John Doe true.

Transforming plain object to specific class is not applying automatically.
You need to use pipes (see docs)
You can write your own, or you can use built-in pipe ValidationPipe.

@unlight Thanks

It works for me.

// ...
async index(@Body(new ValidationPipe({transform: true})) body: BodyDTO) {
  console.log(body instanceof BodyDTO) // true
}
// ...

I suggest updating docs.

The new documentation provides more details about built-in ValidationPipe.
https://docs.nestjs.com/v5

I'll merge docs soon.

docs.nestjs.com/v5

Redirects to docs.nestjs.com

Open in the incognito mode (cache)

@unlight Thanks

It works for me.

// ...
async index(@Body(new ValidationPipe({transform: true})) body: BodyDTO) {
  console.log(body instanceof BodyDTO) // true
}
// ...

I suggest updating docs.

@XGHeaven It's also useful to see the other options the @ValidationPipe() supports. The one I found very useful for security purposes is the whitelist. Here is a use case:

User.create(body).save()

The problem with this is that it's possible to inject into the body of the request properties that are not listed in the DTO, for example, isAdmin: true or whatever flag/role you use to identify admin users.

Ideally, we should strip out from the body non-allowed properties. whitelist: true does just that.

I'm having an issue with Postman where if I send a POST via the command line, for example:

curl -d '{"phone":"value1"}' -H "Content-Type: application/json" -X POST http://localhost:3000/auth/requestCode

It works!

But if I send it through Postman, the @Body() is an empty object. Has anyone seen this?

@tskweres can you show us the POSTMAN query please.

It's also useful to see the other options the @ValidationPipe() supports. The one I found very useful for security purposes is the whitelist.

I second that. It's also possible to forbid processing requests that have unexpected properties using the forbidNonWhitelisted option.

@XGHeaven The docs (https://docs.nestjs.com/techniques/validation#stripping-properties) also mention now that payloads are not automatically transformed to DTOs but it also took me a whole to find it so it was good that you brought it up!

Instead of using new ValidationPipe({transform: true})) on every @Body you can also enable transformation on a global application level:

app.useGlobalPipes(
  new ValidationPipe({
    transform: true,
    whitelist: true
  }),
);

I'm having an issue with Postman where if I send a POST via the command line, for example:

curl -d '{"phone":"value1"}' -H "Content-Type: application/json" -X POST http://localhost:3000/auth/requestCode

It works!

But if I send it through Postman, the @Body() is an empty object. Has anyone seen this?

I was able to get postman to accept body. In body tab select raw option. Then to the right of that I selected application/json in the dropdown.
Entered my object into the text area and it converted it correctly using @Body myVar: MyType

{
    "toAddress": "[email protected]",
    "message": "hello",
    "useSecureEmail": true,
    "contactPhoneNumber": "509931212"
}

I didn't. Ended up using Paw instead of Postman and it works fine

I'm having an issue with Postman where if I send a POST via the command line, for example:
curl -d '{"phone":"value1"}' -H "Content-Type: application/json" -X POST http://localhost:3000/auth/requestCode
It works!
But if I send it through Postman, the @Body() is an empty object. Has anyone seen this?

I was able to get postman to accept body. In body tab select raw option. Then to the right of that I selected application/json in the dropdown.
Entered my object into the text area and it converted it correctly using @Body myVar: MyType

{
    "toAddress": "[email protected]",
    "message": "hello",
    "useSecureEmail": true,
    "contactPhoneNumber": "509931212"
}

Hei, thank you! THANK YOU!

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings