You can see the whole code on the nestjs-auth branch.
Previously, we have only applied Guard to deleteReview
method. I think that we should add the functionality to updateReview
method so that authenticated users can modify their reviews.
However, if we implement the same Guard to the method, it won't work out. The reason is that the Guard we have created only takes deleteReviewArgs
from context, which is the argument objects for the delete operation. We also need to take updateReviewArgs
for the update operation to update. To do this, we have to let the Gaurd know what argument it takes. We can accomplish the mission using mixin
.
Add mixin
In auth/guards/gql.guard.ts
, we will wrap the Guard around a function that takes the name of the arguments and returns mixin
.
export const ReviewGuard: any = (args) => {
@Injectable()
class GqlAuthGuard extends AuthGuard('local') {
getRequest(context: GqlExecutionContext): any {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
// get the argument and save it on request body
req.body = ctx.getArgs()[args];
return req;
}
}
return mixin(GqlAuthGuard);
};
With the mixin
, we now have Guard that can be instantiated and take an argument. We will implement this Guard to both updateReview
and deleteReview
methods and pass in the name of each of the arguments to the Guard.
@Injectable()
export class ReviewResolver {
// ...
@Mutation(() => Review)
@UseGuards(ReviewGuard('updateReviewData'))
@UsePipes(new ValidationPipe({ transform: true }))
async updateReview(
@Args('updateReviewData') updateReviewData: UpdateReviewInput,
): Promise<Review> {
return this.reviewService.updateReview(updateReviewData);
}
@Mutation(() => Review)
@UseGuards(ReviewGuard('deleteReviewData'))
async deleteReview(
@Args('deleteReviewData') deleteReviewData: DeleteReviewInput,
): Promise<Review> {
return this.reviewService.deleteReview(deleteReviewData);
}
}
However, when we try to update a review and do the update operation for the same review, you will get Unauthorized exception from the server.
It is because we have used bcrypt
to hash a password and save it on our database, when the password passed in and not hashed overwrites the hashed password. There are two options to fix this problem. One is to hash the password in updateReview
service. The other is to exclude the password before it reaches the router handler. We will talk about the latter one.
Exclude password using class-transformer and pipe
First of all, we will install class-transformer
.
pnpm install class-transformer
And then, we will annotate password
field with @Exlucde() decorator in UpdateReviewInput
model in dto/args/update-review.input.ts
.
@InputType()
export class UpdateReviewInput {
// ...
@Field()
@Exclude()
password: string;
}
Finally, we will apply ValidationPipes , which validates fields, to updateReview
method. This pipe does exclude fields that are annotated with @Exlucde() decorator and if validated, let them go to the router handler. When we try to do the update operation, it will work out as expected now.
Besides, I have also omitted the @Field() decorator from
password
field inReview
model object so that clients cannot get the password information.
You can see the whole code on the nestjs-auth branch.
THANKS FOR READING. SEE YOU NEXT TIME!