How to transform input data with NestJS and TypeORM

Viewed 11650

I want to create functionality where I can transform incoming data before it hits the database. Let's say that we want to make sure that when a new user is created, the firstName and lastName attribute values are always starting with a capital letter. Or another great example is to prevent login issues and store email addresses always in lowercase letters.

I've taken a look at the pipe documentation of NestJS but it's too generic. I want to be able to specify which fields need transformation and I don't want to create a pipe for each attribute or endpoint that needs transformation.

I've also tried the @Transform decorator form the 'class-transformer' package but this doesn't seem to work.

export class UserRoleCreateDto {
  @IsDefined()
  @IsString()
  @IsNotEmpty()
  @Transform((name) => name.toUpperCase())
  readonly name;
}

The expected result should be the name in uppercase letters but it's not.

Anyone any ideas or examples on how to implement proper input transformation for NestJS / TypeORM before it hits the database?

Thanks for your time!

4 Answers

Here is a small working example

  @UsePipes(new ValidationPipe({transform: true}))
  @Get('test_input')
  async testInput(@Query() data: UserRoleCreateDto): Promise<any> {

    return data;
  }

and checking in bash:

curl 'http://localhost:3060/test_input?name=hello'

{"name":"HELLO"}

Check that you have ValidationPipe decorator and especially transform: true option, without these it doesn't work. transform: true automatically transforms input data to the class instance.

More about validation on the nestjs documentation page - link

Also, just in case, instead of @Query() decorator you can use @Body() for POST data or @Param() for URL parameters

You can also use TypeORM's entity listeners.

@BeforeInsert

You can define a method with any name in entity and mark it with @BeforeInsert and TypeORM will call it before the entity is inserted using repository/manager save.

@Entity()
export class User {
    
    @BeforeInsert()
    nameToUpperCase() {
        this.email = this.email.toLowerCase()
        this.name = this.name.toUpperCase();
    }
}

Source here

This is what you need:

class ClassName {
  @Transform((param) => param.value.toUpperCase())
  name: string;
}

Note that the destructured object in the transform won't care about property name. these are things that you can use in param: value, key, obj, type

ref read more

The correct way to use it is
@Transform(({name}) => name.toUpperCase())
and not
@Transform((name) => name.toUpperCase())

Without the {}, the value is transformed into an object... I found out by adding a console.log() inside the Transform() when my @Matches() validator was throwing an error.

Looks like it's caused by a breaking change in the lib 'class-transformer' as mentionned in this issue : transforming string to lowercase turns string into object [...]

Guys this is because breaking changes in patch version.

Top voted answer's advice is adding @UsePipes(new ValidationPipe({transform: true})) but it changed nothing for me (However, I may already have app.useGlobalPipes)

Related