RESTful Node.js: A Structured Approach
  • Book Cover
  • About the Author
  • Links and Resources
  • Part I: The Why
    • Foreword
    • Preface
    • Chapter 1: Introduction
      • The Rise of REST and Distributed Systems
      • Problem #1: Structureless Design, Structureless REST
      • The Emergence of JavaScript and Node.js
      • Problem #2: Structureless JavaScript, Structureless Node.js
      • Behold, the Solution: A Structured Approach
      • Summary
  • Part 2: The Theory
    • Chapter 2: REST Origins
      • A Brief History of the Web and the Birth of REST
      • REST vs. HTTP
      • REST - The Abstract Web Architecture
      • HTTP - A Peak at REST's Concrete Implementation
      • What does it mean for an API to be RESTful?
      • Measuring "RESTfulness" with Richardson Maturity Model
      • Pragmatic REST vs Dogmatic REST
      • Summary
    • Chapter 3: RESTful API Design Guidelines and "Best Practices"
      • Theories vs. Principles vs. Guidelines
      • URI Design
      • Method Verbs
      • Status Codes
      • Representational Design
      • Metadata Design
      • Versioning Strategies
      • Security Considerations
      • Documentation
      • Case Study: GitHub
      • Summary
    • Chapter 4: Structured JavaScript Architecture
      • The Monstrous Monolith and Its Downfall
      • Layered/N-Tier Architecture: The Unpopular Proven Way
      • Microservices and Distributed Computing: A Popular Misdirection
      • Summary
    • Chapter 5: The 8 Step Recipe
      • Route Name (URI)
      • Input Request
      • Middleware
      • Validation
      • Domain
      • Events
      • Output Response
      • Test, Refactor, Document
      • Summary
  • Part 3: The Code
    • Chapter 6: Introduction to the Bookstore API
      • The Bookstore API Endpoint Specifications
      • API Design and Code Structure
      • Project Setup
      • Summary
    • Chapter 7: Retrieving Books from our API
      • Retrieving All Books - Planning
      • Retrieving All Books - Implementation
      • Retrieving A Book By ID - Planning
      • Retrieving A Book By ID - Implementation
      • Summary
    • Chapter 8: Adding Authentication to our API
      • Registering the User - Planning
      • Registering the User - Implementation
      • Logging the User In - Planning
      • Logging the User In - Implementation
      • Getting Authenticated User - Planning
      • Getting Authenticated User - Implementation
      • Summary
    • Chapter 9: Adding the Create, Update, and Delete Operations to our API
      • Creating A Book Listing - Planning
      • Creating A Book Listing - Implementation
      • Updating A Book Listing By ID - Planning
      • Updating A Book Listing By ID - Implementation
      • Deleting A Book Listing By ID - Planning
      • Deleting A Book Listing By ID - Implementation
      • Summary
    • Chapter 10: Testing our API
      • Testing the Request
      • Testing the Middleware
      • Testing the Validation
      • Testing the Domain
      • Testing the Event
      • Testing the Response
      • Testing the Controller
      • Integration Test
      • Summary
  • Conclusion
    • Final Words
  • Bonus!
    • Refactoring to HATEOAS
  • Appendix
    • Sources & References
Powered by GitBook
On this page
  • 1 - Route Name
  • 2 - Input Request
  • 3 - Middleware
  • 4 - Validation
  • 5 - Domain
  • 6 - Events
  • 7 - Response
Edit on GitHub
  1. Part 3: The Code
  2. Chapter 7: Retrieving Books from our API

Retrieving A Book By ID - Implementation

1 - Route Name

We will first add a route of GET /api/v1/books/:id.

File: src/routes/book.route.js

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

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

router.get('/', getAllBooks)
router.get('/:id', getBookById) // This is our newly added route

module.exports = router

Followed by the controller setup.

File: src/controllers/books/getABookById.js

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

const getBookById = catchException(async (req, res, next) => {
  // our code goes here...
})

2 - Input Request

None.

3 - Middleware

None.

4 - Validation

None.

5 - Domain

The getById() method will look this, nice and simple.

File: src/domain/services/bookRepository.js

// Retrieve - one
const getById = async (id) => {
  return await Model.findById(id)
}

Now for us to actually use the bookRepository in our bookService.

File: src/domain/services/bookService.js

// Retrieve - one
const getBookById = async (bookId) => {
  const book = await bookRepository.getById(bookId)

  return book
}

6 - Events

None.

7 - Response

But wait! let's not forget the 404 validation we promised.

File: src/domain/services/bookService.js

// Retrieve - one
const getBookById = async (bookId) => {
  if (!mongoose.Types.ObjectId.isValid(bookId)) {
    // the id is invalid
    throw new ApiException({
      message: `the book with that id: ${bookId} does not exist.`,
      status: 'failed',
      code: 404,
      data: null,
      errors: [`the book with that id: ${bookId} does not exist.`]
    })
  }

  const book = await bookRepository.getById(bookId)

  if (!book) {
    throw new ApiException({
      message: `the book with that id: ${bookId} does not exist.`,
      status: 'failed',
      code: 404,
      data: null,
      errors: [`the book with that id: ${bookId} does not exist.`]
    })
  }

  return book
}

As usual, here is our controller. Thanks to us doing the business logic validation in our domain layer, our controller is thin and free of clutter.

File: src/controllers/book.controller.js

const getBookById = catchException(async (req, res, next) => {
  const book = await bookService.getBookById(req.params.id)

  return res.json(
    globalResponseDTO({
      status: 'success',
      code: 200,
      message: `Book with the specified id.`,
      data: book,
      errors: null
    })
  )
})
PreviousRetrieving A Book By ID - PlanningNextSummary

Last updated 3 years ago