In NestJS, is it possible to conditionally modify the properties of a request body within controller methods based on the user’s role or access level?
For instance, if a user lacks the necessary permissions, the specific property they are not authorized to alter should be omitted from the request body.
Currently, I employ class-validator and class-transformer for body validation using Data Transfer Objects (DTOs), but I’m uncertain about how to access the request object to perform such conditional logic.
To elaborate, accessing the request object is essential for validating the body, as it may contain critical information like the user’s group or role.
Below is my user model for reference:
enum Role {
USER,
ADMIN
}
model User {
id String @id @default(uuid()) @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
mobileNumber String @unique
role Role @default(USER)
otpCode String?
otpExpiredAt DateTime?
firstName String?
lastName String?
}
And here is my DTO:
export class UpdateUserDto implements Partial<User> {
@IsOptional()
@IsString()
firstName?: string;
@IsOptional()
@IsString()
lastName?: string;
@IsOptional()
@IsMobilePhone()
mobileNumber?: string;
@IsOptional()
@IsEnum($Enums.Role)
role?: $Enums.Role;
}
The requirement is such that only users with an admin role should be permitted to update the role property. If a non-admin user attempts to send the role property, it should be excluded from the request body.
Additionally, while I have the user’s information in the request, I need guidance on accessing this within the DTO, similar to how one might use a group property.
I considered using applyDecorators for this purpose, but I was unable to find a way to access the request within its scope.
export const SerializerDtoByRule = ({ groups }: {groups: $Enums.Role[] }) => {
return applyDecorators(
Expose(({ value }) => {
if (groups.length === 0) return value;
const user = request; // ?? how to accsess request
if (user && groups.includes(user.role)) {
return value;
}
return undefinded;
}),
);
};
Simple question. Are you sure you need to have those fields omitted or passed through at the dto level? How about?
Dto layer is, more or less, a data-transfer object layer. It's good to put there basic validation logic, like is the country code really country code, or check if all data that need to be provided are provided, because it's just easy to do. But there's no need to struggle especially when the goal is to put there some "heavier", business logic, that's rather on services, domain services, cqrs handlers, etc. Another argument that it's not always best to overload DTO is a simple case:
You can write that in dtos, but then somebody will come up and tell: "hey, I want to know who's shady, and I want the logs stored in-app of every e-mail change attempt".
Speaking of accessing
Requestby class-validator - see https://github.com/typestack/class-validator?tab=readme-ov-file#custom-validation-classes. You can always create a class. And if you can that, you can make it injectable too. And then - you know :)