Book List CRUD App with Mongoose - Express Router




Lesson Objectives

  1. Explain What Express.Router does for us
  2. Create External Controller File for Routes
  3. Move Server.js Routes to External Controller File
  4. Require Mongoose in Controller File
  5. Use Controller File in Server.js
  6. Remove References to Base of Controller's URLs



Explain What Express.Router does for us

  • Our server.js file is getting rather bloated
  • express.Router will let us put our routes in a separate file



Create External Controller File for Routes

  1. mkdir controllers
  2. touch controllers/books.js
  3. Edit controllers/books.js

In controllers/books.js:

const express = require('express');
const bookRouter = express.Router();

module.exports = bookRouter;



Move Server.js Routes to External Controller File

Rename app to router. Try using the shortcut! Highlight one occurance of app then click command + d to select the next occurance of it too. Continue doing this until you've highlighted every occurance of the word app. Then type bookRouter to replace all the highlighted code.

While we're here, let's also update the relative pathing to our bookSeed: const bookSeed = require('../models/bookSeed.js')




Here's the final code:

// Dependencies 
const express = require('express');
const bookRouter = express.Router();

// Seed
const bookSeed = require('../models/bookSeed');
bookRouter.get('/books/seed', (req, res) => {
    Book.deleteMany({}, (error, allBooks) => {});

    Book.create(bookSeed, (error, data) => {
        res.redirect('/books');
    });
});

// Index
bookRouter.get('/books', (req, res) => {
    Book.find({}, (error, allBooks) => {
        res.render('index.ejs', {
            books: allBooks,
        });
    });
});

// New
bookRouter.get('/books/new', (req, res) => {
    res.render('new.ejs');
});

// Delete
bookRouter.delete('/books/:id', (req, res) => {
    Book.findByIdAndRemove(req.params.id, (err, data) => {
        res.redirect('/books');
    });
});

// Update
bookRouter.put('/books/:id', (req, res) => {
    if (req.body.completed === 'on') {
        req.body.completed = true;
    } else {
        req.body.completed = false;
    }

    Book.findByIdAndUpdate(req.params.id, req.body, {
        new: true
    }, (error, updatedBook) => {
        res.redirect(`/books/${req.params.id}`);
    });
});

// Create
bookRouter.post('/books', (req, res) => {
    if (req.body.completed === 'on') {
        req.body.completed = true;
    } else {
        req.body.completed = false;
    }

    Book.create(req.body, (error, createdBook) => {
        res.redirect('/books');
    });
});

// Edit 
bookRouter.get('/books/:id/edit', (req, res) => {
    Book.findById(req.params.id, (error, foundBook) => {
        res.render('edit.ejs', {
            book: foundBook
        });
    });
});

// Show
bookRouter.get('/books/:id', (req, res) => {
    Book.findById(req.params.id, (err, foundBook) => {
        res.render('show.ejs', {
            book: foundBook,
        });
    });
});


// Exports 
module.exports = bookRouter;



Require Book Model in Controller File

// Dependencies 
const express = require('express');
const bookRouter = express.Router();
const Book = require('../models/book');
//...



The Book model is no longer needed in server.js. Remove it:

In server.js:

// Dependencies
const express = require('express');
const mongoose = require('mongoose');
const methodOverride = require('method-override');
const app = express();
require('dotenv').config();



Use Controller File in Server.js

This is technically middleware! But make sure it runs after your other middleware.

POP QUIZ: Why do we have to mount our controllers after our other middleware?




In server.js:

// Routes / Controllers 
const booksController = require('./controllers/books');
app.use(booksController);



Specify When our Middleware runs

We only want our books controller to run on the /books route, so let's specify that.

In server.js:

const booksController = require('./controllers/books');
app.use('/books', booksController);



Since we've specified that the controller works with all urls starting with /books, we can remove this from the controller file. Instead of saying things like app.get('/books') we're now going to say app.get('/') because we specified books as the root of our controller.

Confused? Come off mute and ask!

P.S. Don't use the shortcut to remove /books. Whenever we redirect, we need to be specific and include the /books prefix, so we don't want to delete those occurrances.




Here's our completed code so far:

// Dependencies 
const express = require('express');
const bookRouter = express.Router();
const Book = require('../models/book');

// Seed
const bookSeed = require('../models/bookSeed');
bookRouter.get('/seed', (req, res) => {
    Book.deleteMany({}, (error, allBooks) => {});

    Book.create(bookSeed, (error, data) => {
        res.redirect('/books');
    });
});

// Index
bookRouter.get('/', (req, res) => {
    Book.find({}, (error, allBooks) => {
        res.render('index.ejs', {
            books: allBooks,
        });
    });
});

// New
bookRouter.get('/new', (req, res) => {
    res.render('new.ejs');
});

// Delete
bookRouter.delete('/:id', (req, res) => {
    Book.findByIdAndRemove(req.params.id, (err, data) => {
        res.redirect('/books');
    });
});

// Update
bookRouter.put('/:id', (req, res) => {
    if (req.body.completed === 'on') {
        req.body.completed = true;
    } else {
        req.body.completed = false;
    }

    Book.findByIdAndUpdate(req.params.id, req.body, {
        new: true
    }, (error, updatedBook) => {
        res.redirect(`/books/${req.params.id}`);
    });
});

// Create
bookRouter.post('/', (req, res) => {
    if (req.body.completed === 'on') {
        req.body.completed = true;
    } else {
        req.body.completed = false;
    }

    Book.create(req.body, (error, createdBook) => {
        res.redirect('/books');
    });
});

// Edit 
bookRouter.get('/:id/edit', (req, res) => {
    Book.findById(req.params.id, (error, foundBook) => {
        res.render('edit.ejs', {
            book: foundBook
        });
    });
});

// Show
bookRouter.get('/:id', (req, res) => {
    Book.findById(req.params.id, (err, foundBook) => {
        res.render('show.ejs', {
            book: foundBook,
        });
    });
});


// Exports 
module.exports = bookRouter;



STOP! Check your work. Make sure all your routes and redirects work as expected.

Copyright © General Assembly 2022

Created by DanielJS