{ RESTful Routing with Express. }


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

  • Define what REST and RESTful routing are
  • Define what CRUD means
  • Override the default method to allow for PATCH and DELETE requests
  • Build a CRUD app using RESTful routing

Now that we have an idea of how to collect data from our forms we can start to build some applications! We'll start by building an application that does full CRUD on a RESTful resource. But we first need to learn what that means...


REST stands for REpresentational State Transfer. It is a set of principles and guidelines that allow users and systems to access and manipulate web resources. We'll talk more about this later, but you can read about it here and here if you're interested right now.

  • CRUD - An acronym for Create, Read, Update, and Delete. When we talk about building an application with "full CRUD", that means we can perform all of those operations on whatever resources we are working on.

  • Resource - A resource refers to any entity that can be identified, named, and stored. This usually relates directly to database entities, for example a User model in a database.

  • RESTful Routing - A description of how to model URIs for resources. Resource routes are plural by default, so our User model would have a RESTful route of /users. To access an individual user, we would have a route of /users/:id where :id is a route variable representing the ID of the user.

  • HTTP Verb - HTTP methods represent the CRUD operation you can perform on a resource in a single network request. The most common are GET, POST, PUT, PATCH, and DELETE.

Here is a full example for RESTful routes for a User entity:

HTTP Verb Path Description CRUD
POST /users Create a user using a payload Create
GET /users Display a list of users Read
GET /users/:id Display a single user Read
PATCH or PUT * /users/:id Edit a user using a payload Update
DELETE /users/:id Delete a user Delete

* For more information, see the difference between PATCH and PUT

Additionally, two extra paths are often included with the RESTful routes when the server is rendering HTML. These routes are for retrieving forms that, when submitted, trigger a POST or PATCH, respectively:

HTTP Verb Path Description CRUD
GET /users/new Display a form for a new user Read
GET /users/:id/edit Display a form to edit a user Read

Method Override

You may have noticed that to update or delete a user, by REST convention you should issue a PATCH/PUT or DELETE, respectively, as seen in the table above.

The problem is that HTML forms by default only support issuing GET and POST methods. Thus, we need to override these methods and change them to be PATCH and DELETE for certain requests. This can either be done using a header in the request, or through the query string. To make this process easier, we will use a module called method-override.

npm install method-override
const methodOverride = require("method-override");
 use the method override module and specify the name of the key in the query string 
 which we will assign a value of an HTTP verb to override the POST method 
 (_method=DELETE or _method=PATCH) 

To use method override, we simply attach the key of _method and value of an HTTP verb like PATCH or DELETE as the value into the query string. You must make sure that your method on the form is POST as well, even though we will be overriding it. Here is what a form might look like.

form(action=`/create-new-user/${user.id}?_method=PATCH` method="POST")
    input(type="text" name="first" value=`${first}`)
    input(type="text" name="last" value=`${last}`)

CRUD on users

Now that we have an idea of how to build our routes, let's create a simple CRUD application for our users resource. Let's get started with a simple express app in the terminal.

mkdir users_crud && cd users_crud
touch app.js
npm init -y
npm install express pug body-parser method-override
mkdir views
touch views/{base,index,new,edit,show}.pug # create 5 pug files in the views folder
echo node_modules > .gitignore
git init
git add .
git commit -m "initial commit"

Now that we have that folder structure, let's add the following to our app.js

const bodyParser = require('body-parser');
const express = require('express');
const methodOverride = require('method-override');

const app = express();

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

const users = []; // this would ideally be a database, but we'll start with something simple to store our users
var id = 1; // this will help us identify unique users for finding, editing, and deleting

app.get('/', (req, res, next) => {
  // we can use ES2015 arrow function syntax for our callbacks from now on
  return res.redirect('/users');

app.get('/users', (req, res, next) => {
  // { users } is ES2015 object shorthand for { users: users }
  return res.render('index', { users });

app.get('/users/new', (req, res, next) => {
   render a page called new.pug inside the views folder.
   In this file there will be a form that when submitted will POST to /users
  return res.render('new');

app.get('/users/:id', (req, res, next) => {
  Find a user by their id using the find method on arrays.
  We also have to make sure that we convert req.params.id into a
  number from a string so we can safely compare
  const user = users.find(val => val.id === Number(req.params.id));
  // render the display page passing in the user we found
  return res.render('show', { user });

app.get('/users/:id/edit', (req, res, next) => {
  const user = users.find(val => val.id === Number(req.params.id));
  // render the edit page passing in the user we found
  return res.render('edit', { user });

// this route will be requested when a form for creating a user is submitted
app.post('/users', (req, res, next) => {
  // add an object to the users array
    // with the key of name and the value of the data from the form
    name: req.body.name
    // and a key of id and a value of id (the variable we are using as a counter)
  // increment the id for the next user to be added
  return res.redirect('/users')

// this route will be requested when a form updating a user is submitted
app.patch('/users/:id', (req, res, next) => {
  // find the user
  const user = users.find(val => val.id === Number(req.params.id));
  // update their name with the data from the form
  user.name = req.body.name;
  return res.redirect('/users');

// this route will be requested when a form for deleting a user is submitted
app.delete('/users/:id', (req, res, next) => {
  // instead of finding the entire user, let's just see what index they are at in our users array
  const userIndex = users.findIndex(val => val.id === Number(req.params.id));
  // then we can remove one item from the users array starting at the correct index we found above
  return res.redirect('/users');

app.listen(3000, () => {
  console.log('Server is listening on port 3000...');

You may also notice that we are starting to place a next parameter in our callback functions. We will see in a later section why this is so helpful, but in a nutshell it is useful for handling errors and making sure they are propagated to the top.


Here is a screencast that covers the past few sections with an Express app:

Sample App

You can see an example of RESTful routing here.

When you're ready, move on to Helpful Express Middleware


Creative Commons License