{ Express Router. }

Objectives:

By the end of this chapter, you should be able to:

  • Move routing logic into a separate file using the express router
  • Use the express router to refactor routing code

Router

So far we have seen how to create routes in express and render templates and even respond with redirects! As we start building more and more routes, our app.js can get very messy quickly. In order to clean this up, we will move our routes to a file in a folder called routes. The files in this folder will contain all of our routes and we will export them using the express router. Let's get started with a simple application.

RESTful Review

We saw earlier that there is an idea of how to standardize our routes using something called RESTful routing. REST or REpresentational State Transfer is a standard for designing web applications, but the important takeaway here is that standardizing our routes is very important and allows us to work with other developers and across a vareity of projects using the same structure. Let's review what RESTful routes for a users resource might look like:

HTTP Verb Path Description
GET /users Show all users
GET /users/new Show a form for creating a new user
GET /users/:id Show a single user
GET /users/:id/edit Show a form for editing a user
POST /users Create a user when a form is submitted
PATCH /users/:id Edit a user when a form is submitted
DELETE /users/:id Delete a user when a form is submitted

Getting started

Now that we have a good structure for our RESTful routes, let's use the express router to re-create our users application. Let's get started with a new express app. In the terminal, let's run the following

mkdir express-router && cd express-router
touch app.js
npm init -y
npm install express pug body-parser method-override
mkdir views routes
touch routes/users.js
touch views/{base,index,new,edit,show}.pug

Now let's add the following to our routes/users.js

// we need to bring in express 
var express = require("express");
// so that we can create a router object. This router object will have similar methods (.get, .post, .patch, .delete) to the app object we have previously been using.
var router = express.Router();

var users = []; // this would ideally be a database, but we'll start with something simple
var id = 1; // this will help us identify unique users

// instead of app.get...
router.get('/users', function(req, res, next){
  res.render('index', {users}); // {users:} is ES2015 object shorthand for {users:users}
});

router.get('/users/new', function(req, res, next){
  res.render('new');
});

router.get('/users/:id', function(req, res, next){
  var user = users.find(val => val.id === Number(req.params.id));
  res.render('show', {user});
});

router.get('/users/:id/edit', function(req, res, next){
  var user = users.find(val => val.id === Number(req.params.id));
  res.render('edit', {user});
});

// instead of app.post...
router.post('/users', function(req, res, next){
  users.push({
    name: req.body.name
    id: ++id
  });
  res.redirect('/users');
});

// instead of app.patch...
router.patch('/users/:id', function(req, res, next){
  var user = users.find(val => val.id === Number(req.params.id));
  user.name = req.body.name;
  res.redirect('/users');
});

// instead of app.delete...
router.delete('/users/:id', function(req, res, next){
  var userIndex = users.findIndex(val => val.id === Number(req.params.id));
  users.splice(userIndex,1);
  res.redirect('/users');
});

// Now that we have built up all these routes - let's export this module for use in our app.js!
module.exports = router;

Now, how do we actually use these routes? Let's add the following in our app.js

var express = require("express");
var app = express();
var methodOverride = require("method-override");
var morgan = require("morgan");
var bodyParser = require("body-parser");

// require our routes/users.js file 
var userRoutes = require("./routes/users");

app.set("view engine", "pug");
app.use(express.static(__dirname + "/public"));
app.use(morgan("tiny"));
app.use(bodyParser.urlencoded({extended:true}));
app.use(methodOverride("_method"));

// Now let's tell our app about those routes we made!
app.use(userRoutes);

app.get("/", function(req, res, next) {
  res.redirect('/users');
});

app.listen(3000, function() {
  console.log("Server is listening on port 3000");
});

We could also write these routes using an alternate syntax for express routing.

var express = require("express");
var router = express.Router();

var users = []; // this would ideally be a database, but we'll start with something simple
var id = 1; // this will help us identify unique users

router.route('/users')
    .get(function(){
      res.render('index', {users});
    })
    .post(function(){
      users.push({
        name: req.body.name
        id: ++id
      });
      res.redirect('/users');
    });

router.route('/users/new').get(function(req, res, next) {
  res.render('new');
});

router.route('/users/:id/edit').get(function(req, res, next) {
  var user = users.find(val => val.id === Number(req.params.id));
  res.render('edit', {user});
});

router.route('/users/:id')
    var user = users.find(val => val.id === Number(req.params.id));
    .get(function(req, res, next){
        res.render('show', {user});
    });
    .patch(function(req, res, next){
      user.name = req.body.name;
      res.redirect('/users')
    });
    .delete(function(req, res, next){
      users.splice(user.id,1)
      res.redirect('/users')
    });

module.exports = router;

Even better - we can prefix all of our routes to start with /users if we would like. Here is what that looks like in our routes/users.js

// we need to bring in express 
var express = require("express");
// so that we can create a router object. This router object will have similar methods (.get, .post, .patch, .delete) to the app object we have previously been using.
var router = express.Router();

var users = []; // this would ideally be a database, but we'll start with something simple
var id = 1; // this will help us identify unique users

// no need to start with /users since we will prefix all routes in this file with /users
router.get('/', function(req, res, next) {
  res.render('index', {users}); // {users:} is ES2015 object shorthand for {users:users}
});

// no need to start with /users since we will prefix all routes in this file with /users
router.get('/new', function(req, res, next) {
  res.render('new');
});

// no need to start with /users since we will prefix all routes in this file with /users
router.get('/:id', function(req, res, next) {
  var user = users.find(val => val.id === Number(req.params.id));
  res.render('show', {user});
});

// no need to start with /users since we will prefix all routes in this file with /users
router.get('/:id/edit', function(req, res, next) {
  var user = users.find(val => val.id === Number(req.params.id));
  res.render('edit', {user});
});

// no need to start with /users since we will prefix all routes in this file with /users
router.post('/', function(req, res, next) {
  users.push({
    name: req.body.name
    id: ++id
  });
  res.redirect('/users');
});

// no need to start with /users since we will prefix all routes in this file with /users
router.patch('/:id', function(req, res, next) {
  var user = users.find(val => val.id === Number(req.params.id));
  user.name = req.body.name;
  res.redirect('/users');
});

// no need to start with /users since we will prefix all routes in this file with /users
router.delete('/:id', function(req, res, next) {
  var userIndex = users.findIndex(val => val.id === Number(req.params.id));
  users.splice(userIndex,1);
  res.redirect('/users');
});

// Now that we have built up all these routes - let's export this module for use in our app.js!
module.exports = router;

And here in our app.js

// prefix every single route in here with /users
app.use('/users', userRoutes);

Sample App

You can see an example of the express router here.

When you're ready, move on to Express.js Middleware Exercises

Continue