I have the following controller to create a new user. The request body (CreateUser
) has an optional field locationName
. I have a pipe which changes the locationName
to null
if it is not defined in the request body.
When I use @Body
decorator, I can get the body properly with the locationName
never being undefined
in the runtime.
For Swagger, I can see that the locationName
is optional. But when I try to access createUserInput.locationName
in the code Typescript still complains that the value can be undefined
(which I understand).
How can I introduce a different type for the controller code and another type for Swagger?
User
controller:@Post("/users")
async createUser(
@Body(CreateUserLocationPipe) createUserInput: CreateUser
): Promise<User> {
return await this.usersService.create(createUserInput);
}
CreateUser
class:export class CreateUser {
@ApiProperty({ description: "The email of the user" })
email: string;
@ApiProperty({ description: "Location of the user" })
locationName: string | undefined | null;
}
CreateUserLocationPipe
pipe:@Injectable()
export class CreateUserLocationPipe {
transform(value: string | null | undefined): string | null {
if (value.locationName === undefined) {
value.locationName = null;
}
}
}
We can create a different class for the input that comes to the controller.
export class CreateUserControllerInput extends OmitType(CreateUser, ["locationName"] as const) {
locationName: WellKnownLocationName | null;
locationPlantId: string | null;
}
Then we can differentiate the controller body from the request body of swagger like below.
@Post("/users")
@ApiBody({ type: CreateUser }) // This is exposed by swagger as the request body
async createUser(
@Body(CreateUserLocationPipe) createUserInput: CreateUserControllerInput // This is the type for the controller
): Promise<User> {
return await this.usersService.create(createUserInput);
}