Registering the User - Implementation
1 - Route Name
GET /api/v1/users
Let's add an additional users route to the routes/index.js file.
File: src/routes/index.js
const express = require('express')
const router = express.Router()
const userRoutes = require('./user.route')
const bookRoutes = require('./book.route')
function getRouter() {
router.use('/books', bookRoutes)
router.use('/users', userRoutes) // our new route
return router
}
module.exports = getRouterHere, we will then add a registerUser controller function.
File: src/routes/user.route.js
We will then proceed to fill up the rest of the controller below.
File: src/controllers/auth.controller.js
2 - Input Request
Now we will add in a custom DTO that will help us specify the contract between us and the client.
This layer can be seem as a validation layer, but what we are ultimately trying to accomplish here is to get the frontend and backend aligned on the same page. Notice there is no real validation of having the password being a certain length or the phone_number being a certain format. The DTO simply helps the client who's calling our API to provide the correct structure in the payload.
File: src/requests/registerUserDTO.js
3 - Middleware
As stated in the previous section, we will be adding in a global middleware to help us catch errors.
You may have noticed a utility wrapper being used in the controllers up until now called catchExceptions.
File: src/controllers/auth.controller.js
File: src/utils/catchExceptions.js
The reason for this is due to our globalErrorHandler that we'll be putting as follows.
File: src/server.js
File: src/utils/globalErrorHandler.js
This is really going to make our lives easier, because every time we throw an ApiException anywhere in our application, it is going to automatically catch the error we have thrown and output it out as JSON output.
4 - Validation
Next is the form validation, we'll be using the validatorjs library to help us achieve the validation rules we specified in our planning section of this endpoint.
File: src/validators/registerUserValidator.js
5 - Domain
For our domain layer, we'll first create our user model as follows.
File: src/domain/models/user.model.js
Then, we will create the createUser method, which will simply save the userData into our database.
File: src/domain/services/userRepository.js
Lastly, we will create the authService layer and use our userRepository.createUser() utility to create our service for registering our user.
File: src/domain/services/authService.js
Notice how we do a try-catch here where we throw a custom ApiException in order to catch a specific type of error, in this case it's that the email must be unique.
6 - Events
As noted in the planning section of this endpoint, when the user has successfully registered, an event will fire off. We can take advantage of Node.js' built-in eventEmitter to create a pub-sub structure for us.
File: src/events/index.js
File: src/events/userHasRegisteredEvent.js
Notice here that we are not really going to be sending any real emails, we will just be stubbing it out. Of course, if you really wanted to, I suggest using SendGrid or Amazon SES.
File: src/utils/mailer.js
7 - Response
Recall in the previous section Registering the User - Planning. We indicated that there were going to be 4 different responses.
Remember, our globalErrorHandler is able to catch all these ApiExceptions.
The first, DTO validation is triggered when we throw the following ApiException.
File: src/requests/registerUserDTO.js
The second, form validation is triggered when we throw the following ApiException.
File: src/validators/registerUserValidator.js
The third, service layer validation in which we check if email already taken, is triggered when we throw the following ApiException.
File: src/domain/services/authService.js
Finally, the last response, which is the success response.
If everything goes through and there are no errors, then we will return a User object.
For that, we will use a DTO as follows.
File: src/responses/userResponseDTO.js
Putting It All Together
If you've followed a long, this is how our controller should look like by the end.
Nice and clean 😎.
Last updated