24

Creating a new user will ignore non-specified objects from create-user.dto.ts

However when I update the user it will add unwanted fields like this:

// update-user.dto.ts
import { IsEmail } from 'class-validator';
import { Address } from '../model/address';

export class UpdateUserDto {
  firstName: string;

  lastName: string;

  @IsEmail(undefined, { message: 'Not a valid e-mail' })
  email: string;

  username: string;

  password: string;

  addresses: Address[];
}

This is the update action from the user service

// user.service.ts
  async update(data: UpdateUserDto) {
    try {
      this.logger.log(data);
      const id = '5c6dd9852d4f441638c2df86';
      const user = await this.userRepository.update(id, data);

      return { message: 'Updated your information' };
    } catch (error) {
      this.logger.log(error);
      throw new HttpException('', HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

And here is the user.controller.ts

  @Patch()
  @UsePipes(CustomValidationPipe)
  async update(@Body() data: UpdateUserDto) {
    return this.userService.update(data);
  }

The client patch data:

// Unwanted junk from client
{
  "email": "newemail@gmail.com",
  "junk": "junk"
}

The email will update properly but the row will have a new unwanted property junk with the value junk

Kim Kern
  • 54,283
  • 17
  • 197
  • 195
Tea Zulo
  • 65
  • 1
  • 6
  • 14

5 Answers5

28

I'm assuming you are using class-transformer's validate method in your CustomValidationPipe.

When you pass the whitelist option to it, validate will strip away all unknown (-> no annotation in your DTO class) properties:

validate(userUpdate, { whitelist: true })

If you want to throw a validation error instead of just stripping away unknown properties, you can additionally pass the forbidNonWhitelisted option.

validate(userUpdate, { whitelist: true, forbidNonWhitelisted: true });

In the case of an update, you probably also want to use skipMissingProperties: true, so that validate will not throw an error, when e.g. lastName is not part of the update.


Note, that you should annotate all properties in your dto class, for the validation to work properly:

@IsString()
lastName: string;

@ValidateNested()
@Type(() => Address)
address: Address
Kim Kern
  • 54,283
  • 17
  • 197
  • 195
16

Not sure when this behavior/option was added to NestJS (perhaps it was added after the original question and accepted answer), but the best way to achieve unknown property stripping would be that:

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

That is it. By just making sure you have whitelist: true in your config, you won't be getting any unknown/invalid property.

You can also completely stop the request by setting another property called forbidNonWhitelisted to true.

More on that here: https://docs.nestjs.com/techniques/validation#stripping-properties

Bruno Krebs
  • 3,059
  • 1
  • 24
  • 36
2

For newbies like me who still wondering, here some easy way to implement it:

@UsePipes(new ValidationPipe({ whitelist: true }))

Put the decorator under decorator of methods (@Post, @Put , etc)

Both UsePipe and ValidationPipe are imported @nestjs/common

xekaiforce
  • 89
  • 4
1

If you also want to throw an error for unexpected properties it can be configured as follows:

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

An unexpected property will result in

{
 "statusCode": 400,
 "message": [
    "property XXX should not exist"
 ],
 "error": "Bad Request"
}

The discussion can be found here

Pablo Albaladejo
  • 2,950
  • 1
  • 17
  • 21
-1

I found the solution:

This is how user.service.ts update() should look like:

const user = await this.userRepository.create(data);

needs to be added before

await this.userRepository.update(id, user);

Here is the complete user.service.ts update()

  async update(data: UpdateUserDto) {
    this.logger.log(data);

    // added for testing purposes (id should be based on active user)
    const id = '5c6ef2c823bf4e3414d65cd0';
    const user = await this.userRepository.create(data);
    await this.userRepository.update(id, user);

    return { message: 'Updated your information' };
  }

Now any unwanted properties will not be added to the row

Tea Zulo
  • 65
  • 1
  • 6
  • 14