{ RESTful Routing with Express. }

Objectives:

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

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

CRUD

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 resource. Let's first define what we mean by CRUD and resource.

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 a noun that we are operating on. For example if our resource was "users" and we wanted full CRUD on the "users" resources, we would have routes for the following (these routes are using a convention called RESTful routing):

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

methodOverride

You may have noticed that in the last two routes in the above table, we are using different HTTP verbs! These verbs are PATCH and DELETE and serve the purpose of updating a resource and deleting a resource.

The problem is that HTML forms only know the GET and POST methods, so 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 --save method-override
// include the method override module
var methodOverride = require("method-override");
// use the method override module and specify the name of the key in the query string which we will assing a value of an HTTP verb to override the POST method (_method=DELETE or _method=PATCH)
app.use(methodOverride("_method"));

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 --save 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

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

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

var 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("/", function(req, res, next){
  res.redirect("/users");
});

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

app.get('/users/new', function(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
  res.render('new');
});

app.get('/users/:id', function(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
  var user = users.find(val => val.id === Number(req.params.id));
  // render the show page passing in the user we found
  res.render('show', {user});
});

app.get('/users/:id/edit', function(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
  var user = users.find(val => val.id === Number(req.params.id));
  // render the edit page passing in the user we found
  res.render('edit', {user});
});

// this route will be requested when a form for creating a user is submitted
app.post('/users', function(req, res, next){
  // add an object to the users array
  users.push({
    // 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)
    id,
  });
  // increment the id for the next user to be added
  id++;
  res.redirect('/users')
});

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

// this route will be requested when a form for deleting a user is submitted
app.delete('/users/:id', function(req, res, next){
  // instead of finding the entire user, let's just see what index they are at in our users array
  var 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
  users.splice(userIndex,1);
  res.redirect('/users');
});

app.listen(3000, function(){
  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.

Sample App

You can see an example of RESTful routing here.

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

Continue