# Creating A Book Listing - Implementation

## 1 - Route Name

`POST /api/v1/books`

Just like we planned, let's add in the route.

*File: src/routes/book.route.js*

```javascript
const express = require('express')
const router = express.Router()

const { getAllBooks, getBookById, createABook } = require('../controllers/book')

router.get('/', getAllBooks)
router.get('/:id', getBookById)

// This is the new route we are adding in
router.post('/', createABook)
```

Let's also create our controller so we can fill in the details later on.

*File: src/controllers/book/createABook.js*

```javascript
const catchException = require('../../utils/catchExceptions')

/**
 * Creates a new book listing.
 */
const createABook = 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*

```javascript
const ApiException = require('../utils/ApiException')

const fields = ['title', 'description', 'price', 'author', 'datePublished']

const createBookRequestDto = (data) => {
  const errors = []
  fields.forEach((field) => {
    if (!(field in data)) {
      errors.push(`This DTO's property is required: ${field}.`)
    }
  })

  if (errors.length > 0) {
    throw new ApiException({
      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*

```javascript
const express = require('express')
const router = express.Router()

const { createABook } = require('../controllers/book')

const isAuthenticated = require('../middleware/auth.middleware')

// The isAuthenticated middleware is going to protect this route
// from unauthenticated users from accessing it
router.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.

*File: src/validators/createBookValidator.js*

```javascript
const Validator = require('validatorjs')
const ApiException = require('../utils/ApiException')

/**
 * @param {*} data {
 *  - title
 *  - description
 *  - price
 *  - author
 *  - datePublished
 * }
 *
 * @returns Validator
 */
const createBookValidator = (data) => {
  const rules = {
    title: 'required',
    description: 'required',
    price: 'required|numeric|min:1',
    author: 'required',
    datePublished: 'required'
  }

  const validator = new Validator(data, rules)

  if (validator.fails()) {
    let errors = []
    for (const field in validator.errors.errors) {
      errors = errors.concat(validator.errors.errors[field])
    }

    throw new ApiException({
      message: 'There were errors with the validation',
      status: 'error',
      code: 400,
      data: null,
      errors
    })
  }

  return validator
}

module.exports = createBookValidator
```

## 5 - Domain

As mentioned in the previous planning section, this will be the model we create.

*File: src/domain/models/book.model.js*

```javascript
const mongoose = require('mongoose')
const Schema = mongoose.Schema

const bookModel = new mongoose.Schema({
  userId: {
    type: Schema.Types.ObjectId,
    ref: 'user'
  },
  title: {
    type: String,
    required: true
  },
  description: {
    type: String
  },
  price: {
    type: Number,
    required: true
  },
  author: {
    type: String
  },
  datePublished: {
    type: String
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
})

module.exports = mongoose.model('book', bookModel)
```

This will be the repository layer on top of that model.

*File: src/domain/repositories/book.repository.js*

```javascript
const Model = require('../models/book.model')

// Saves a book in the database
const create = async (newBook) => {
  const book = new Model(newBook)
  return await book.save()
}

module.exports = {
  create
}
```

Finally, our service layer that will use the `bookRepository` layer.

*File: src/domain/services/book.service.js*

```javascript
const bookRepository = require('../repositories/book.repository')
const ApiException = require('../../utils/ApiException')
const mongoose = require('mongoose')

// Create a book
const createBook = async (book) => {
  return bookRepository.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.

```javascript
const mongoose = require('mongoose')
const globalResponseDto = require('../../responses/globalResponseDto')
const createBookRequestDto = require('../../requests/createBookRequestDto')
const catchException = require('../../utils/catchExceptions')
const bookService = require('../../domain/services/book.service')
const bookResponseDto = require('../../responses/bookResponseDto')
const createBookValidator = require('../../validators/createBookValidator')

/**
 * Creates a new book listing.
 */
const createABook = catchException(async (req, res) => {
  const createBookRequest = createBookRequestDto({
    id: req.session.user.id,
    ...req.body
  })

  createBookValidator(createBookRequest)

  const book = await bookService.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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.restfulnode.com/part-3/chapter-9/2-creating-a-book-listing-implementation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
