I have endpoint where user can sign up. It returns promise with User
generic type, but, because of fact, that it returns User
created instance, there are a couple of fields that I'd like to remove, for example password.
@Post('/sign-up')
signUp(@Body() signUpUserDto: SignUpUserDto): Promise<User> {
return this.userService.signUp(signUpUserDto);
}
I tried 2 ways.
So, according to documentation, I used @UseInterceptors(ClassSerializerInterceptor)
interceptor and class-transform
@Exclude
decorator, but it doesn't work (neither decorator is put on DTO or User
)
@UseInterceptors(ClassSerializerInterceptor)
@Post('/sign-up')
signUp(@Body() signUpUserDto: SignUpUserDto): Promise<User> {
return this.userService.signUp(signUpUserDto);
}
DTO:
import { Exclude } from "class-transformer";
export class SignUpUserDto {
readonly email: string;
@Exclude()
readonly password: string;
Or model:
@Table
export class User extends Model<User, IUserCreatingAttributes> {
@PrimaryKey
@Default(DataType.UUIDV4)
@Column({ type: DataType.UUID })
id: string;
@Column({ type: DataType.STRING, allowNull: false, unique: true })
email: string;
@Exclude()
@Column({ type: DataType.STRING, allowNull: false })
password: string;
Actually, I tried to do the same, but with custom interceptor. Had no result also:
import { instanceToPlain } from 'class-transformer';
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response> {
intercept(
context: ExecutionContext,
next: CallHandler
): Observable<Response> {
return next.handle().pipe(
map((data) => ({
statusCode: context.switchToHttp().getResponse().statusCode,
message: data.message || '',
data: instanceToPlain(data)
}))
);
}
}
I have a guess that it doesn't work, because I return promise, and not instance, but I am not sure, maybe I just do something wrong.
So, what is the problem? How can I serialize this data?
EDIT.
I use Sequelize
as ORM, if it is important.
EDIT 2.
This is how signUp
function looks like:
constructor(
@InjectModel(User) private userRepository: typeof User,
@InjectModel(UserBan) private userBanRepository: typeof UserBan,
@Inject(forwardRef(() => AuthService))
private authService: AuthService,
@Inject(forwardRef(() => RoleService))
private roleService: RoleService
) {}
...
async signUp(signUpUserDto: SignUpUserDto): Promise<User> {
const sameEmailUsernameUser = await this.userRepository.findOne({
where: {
[Op.or]: [
{ email: signUpUserDto.email },
{ username: signUpUserDto.username }
]
}
});
if (sameEmailUsernameUser)
throw new HttpException('user-already-exists', HttpStatus.BAD_REQUEST);
const hashedPassword = await bcrypt.hash(signUpUserDto.password, 5);
const user = await this.userRepository.create({
...signUpUserDto,
password: hashedPassword
});
let role = await this.roleService.getRole({ value: 'USER' });
/** Instead of seeder */
if (!role) {
role = await this.roleService.createRole({
value: 'USER',
description: 'Common user'
});
}
await user.$set('roles', [role.id]);
user.roles = [role];
return user;
}
I have found other solution, I can not @Exclude
fields that I don't want in response, but @Expose
fields, that I need.
import { Exclude, Expose } from "class-transformer";
interface IUserCreatingAttributes {
email: string;
password: string;
}
@Exclude()
@Table
export class User extends Model<User, IUserCreatingAttributes> {
@PrimaryKey
@Default(DataType.UUIDV4)
@Column({ type: DataType.UUID })
@Expose()
id: string;
@Column({ type: DataType.STRING, allowNull: false, unique: true })
@Expose()
email: string;
@Column({ type: DataType.STRING, allowNull: false })
password: string;
@Column({ type: DataType.STRING, allowNull: false, unique: true })
@Expose()
username: string;
...
And don't forget about @UseInterceptors(ClassSerializerInterceptor)
.