Validation middleware
We have to resort to a middleware when we cannot have automatic validation.
If you have noticed, we are not validating the login credentials in a DTO before we actually check if a user
with that email
exists and the password
is correct. In this case, we unfortunately cannot use the @Body()
decorator to validate the login credentials because guards are activated before pipes, and therefore the LocalAuthGuard
is activated before the ValidationPipe
. A solution we can use, although not as tidy as just using a DTO directly with the @Body()
decorator, is to use a Middleware, which is the only thing that is activated before a guard. Let's then begin.
Read about the Request lifecycle to better understand this execution order.
First, we will actually create a DTO for the login credentials. Create then, the file auth -> dto -> login.dto with the respective validation.
Another approach we could use, is to make the LoginDto
be a PickType
of the CreateUserDto
, as the fields with their respective validations are already there. It may be a bit cleaner but maybe also a bit more coupled solution. The decision is left to the reader.
After that, let's create the middleware.
Remember to replace the types of the parameters with Request
, Response
and NextFunction
from express
.
Inside the use()
method, first we'll transform the req.body
into an instance of the LoginDto
.
Then, we'll manually use the validate()
method from class-validator to obtain potential errors from the validation of the loginDto
. Notice that we also use options to ensure that no strange fields will be accepted.
Then, if there are errors, we send their messages in a BadRequestException
. We want to obtain only the values (Object.values()
) from the constraints
field, and then merge the resulting arrays into a single array with the error messages (flatMap()
).
Alright, we have our LoginValidationMiddleware
. To use it, we need to go to the AuthModule
. First, make it implement the NestModule
interface. Then, inside the class, implement the configure()
method, which receives a MiddlewareConsumer
. Make the consumer
call the apply()
method with the middleware to be used. Then chain this with a call to the forRoutes()
method, with the path to the route that will receive the middleware.
And there we have it, our fields are being validated.
Commit - Validating login credentials with middleware
An improvement that could be done, is to create a generic ValidationMiddleware
factory function that would have a DTO class as parameter, and return a validation middleware class responsible for validating that specific DTO. To achieve this, we should create a function that has a type parameter that extends the Type
from NestJS (which represents a class), and a parameter of its type. After that, return the middleware class as it was, altering just the argument of validate()
accordingly.
We should also remember to rename the file itself and also the folder.
Commit - Generic middleware with factory function
Last updated