Dear @kelvin-mai ,
first of all: This tutorial is awesome ๐ Really nice work!
I have a question regarding the use of the Partial<Entity>
in your update(...)
methods, like in the linked code snippet for the controller:
|
@Put(':id') |
|
@UseGuards(new AuthGuard()) |
|
@UsePipes(new ValidationPipe()) |
|
updateIdea( |
|
@Param('id') id: string, |
|
@User('id') user, |
|
@Body() body: Partial<IdeaDTO>, |
|
) { |
|
this.logData({ id, user, body }); |
|
return this.ideaService.update(id, user, body); |
|
} |
and the corresponding service:
|
async update( |
|
id: string, |
|
userId: string, |
|
data: Partial<IdeaDTO>, |
|
): Promise<IdeaRO> { |
|
let idea = await this.ideaRepository.findOne({ |
|
where: { id }, |
|
relations: ['author', 'upvotes', 'downvotes', 'comments'], |
|
}); |
|
if (!idea) { |
|
throw new HttpException('Not found', HttpStatus.NOT_FOUND); |
|
} |
|
this.ensureOwnership(idea, userId); |
|
await this.ideaRepository.update({ id }, data); |
|
idea = await this.ideaRepository.findOne({ |
|
where: { id }, |
|
relations: ['author', 'upvotes', 'downvotes', 'comments'], |
|
}); |
|
return this.ideaToResponseObject(idea); |
|
} |
As far as i understood, the Partial<IdeaDTO>
only tells, that it must be an IdeaDTO
, but it must not be "fully filled" (e.g., not all values must be present). Wouldn't it be better to "remove" the Partial
part and directly use the IdeaDTO
in this case and use the @IsOptional()
decorator from the class-validator
package?
My thought process is as follows:
Consider the example, where you have different DTOs
based on the operation you are calling (e.g., a CreateUserDTO
and UpdateUserDTO
). For the CreateUserDTO
you may provide different attributes than for the UpdateUserDTO
(like, the user is not allowed to change his username after registration).
This way you could enforce the user, that he must send certain attributes with the request (e.g., they must be present!). For example, in order to change your user-profile you must send the current password to prove your identity!
Further, it would be much cleaner, if you use Swagger
as well - you would use the @ApiModelPropertyOptional()
decorator on the attributes that are flagged as @IsOptional()
. This way,
This would look something like this:
export class UpdateUserDTO {
@IsOptional()
@IsString()
@ApiModelPropertyOptional({ type: 'string' })
firstname?: string;
@IsOptional()
@IsString()
@ApiModelPropertyOptional({ type: 'string' })
lastname?: string;
// additional attributes that may be set optionally here
@IsString()
@MinLength(8)
@ApiModelProperty({ type: 'string', minLength: 8 })
password: string;
}
What do you think about this? Or am I missing something crucial here?!
Of course, the corresponding Entity
class may needs to have optional attributes as well (e.g., it may be the case that the firstname
will never be set) - or you use default
values from TypeORM
..
I would love to hear your thoughts on this...
All the best