constexpress=require('express')constrouter=express.Router()const { getAllBooks,getBookById,createABook } =require('../controllers/book')router.get('/', getAllBooks)router.get('/:id', getBookById)// This is the new route we are adding inrouter.post('/', createABook)
Let's also create our controller so we can fill in the details later on.
File: src/controllers/book/createABook.js
constcatchException=require('../../utils/catchExceptions')/** * Creates a new book listing. */constcreateABook=catchException(async (req, res) => {// we'll fill in the details after we get each of the other layers ready})module.exports= createABook
2 - Input Request
Very similar to how we implemented our registerUserRequestDto, we'll create a createBookRequestDto that has the fields title, description, price, author, and datePublished in it.
File: src/requests/createBookRequestDto.js
constApiException=require('../utils/ApiException')constfields= ['title','description','price','author','datePublished']constcreateBookRequestDto= (data) => {consterrors= []fields.forEach((field) => {if (!(field in data)) {errors.push(`This DTO's property is required: ${field}.`) } })if (errors.length>0) {thrownewApiException({ status:'error', code:422, message:'Input fields are of not the correct form.', data:null, errors }) }return data}module.exports= createBookRequestDto
3 - Middleware
We are going to be reusing the isAuthenticated middleware from before and add it in right before our controller.
File: src/routes/book.route.js
constexpress=require('express')constrouter=express.Router()const { createABook } =require('../controllers/book')constisAuthenticated=require('../middleware/auth.middleware')// The isAuthenticated middleware is going to protect this route// from unauthenticated users from accessing itrouter.post('/', isAuthenticated, createABook)
4 - Validation
Let's not forget about the validation layer. Again, this is very similar to the registerUserValiator function we created in the previous chapter except it's just going to be for different fields.
This will be the repository layer on top of that model.
File: src/domain/repositories/book.repository.js
constModel=require('../models/book.model')// Saves a book in the databaseconstcreate=async (newBook) => {constbook=newModel(newBook)returnawaitbook.save()}module.exports= { create}
Finally, our service layer that will use the bookRepository layer.
File: src/domain/services/book.service.js
constbookRepository=require('../repositories/book.repository')constApiException=require('../../utils/ApiException')constmongoose=require('mongoose')// Create a bookconstcreateBook=async (book) => {returnbookRepository.create(book)}module.exports= { createBook}
6 - Events
None.
7 - Response
If we put this all together now in the controller, this is what we'll get.
constmongoose=require('mongoose')constglobalResponseDto=require('../../responses/globalResponseDto')constcreateBookRequestDto=require('../../requests/createBookRequestDto')constcatchException=require('../../utils/catchExceptions')constbookService=require('../../domain/services/book.service')constbookResponseDto=require('../../responses/bookResponseDto')constcreateBookValidator=require('../../validators/createBookValidator')/** * Creates a new book listing. */constcreateABook=catchException(async (req, res) => {constcreateBookRequest=createBookRequestDto({ id:req.session.user.id,...req.body })createBookValidator(createBookRequest)constbook=awaitbookService.createBook({ userId:mongoose.Types.ObjectId(req.session.user._id),...req.body })res.status(200).json(globalResponseDto({ status:'success', code:200, message:`Book has successfully been added to the database.`, data:bookResponseDto(book), errors:null }) )})module.exports= createABook
The createBookRequestDto and the createBookValidator will naturally throw an ApiException which will yield the correct output response as we stated previously in the planning section of this endpoint.
In case of no errors and we get a successful pass, we will reuse our bookResponseDto from which we created previously on the output of the bookService.create method we just implemented and return the newly created book in our response.