Put and Delete

Objectives

  • Use method-override to implement PUT and DELETE routes in express.

Review REST

VERB
URL
Action (CRUD)
Description

GET

/dinosaurs

Index (Read)

lists all dinosaurs

GET

/dinosaurs/new

New (Read)

shows a form to make a new dinosaur

POST

/dinosaurs

Create (Create)

creates an dinosaur with the POST payload data

GET

/dinosaurs/:id

Show (Read)

list information about a specific dinosaur (i.e. /dinosaurs/1)

GET

/dinosaurs/edit/:id

Edit (Read)

shows a form for editting a specific dinosaurs (i.e. /dinosaurs/edit/1)

PUT

/dinosaurs/:id

Update (Update)

updates the data for a specific dinosaur (i.e. /dinosaurs/1)

DELETE

/dinosaurs/:id

Destroy (Delete)

deletes the dinosaur with the specified id (i.e. /dinosaurs/1)

In the previous half of this lesson, we implemented the first four routes. Here we will cover the final three routes in this RESTful routing example.

Method-Override

PUT and DELETE routes are not supported by HTML5. If you're wondering why, check out these discussions on stackoverflow and stackexchange. These requests are so often used that there are well-established work-arounds like method-override, which is what we will use.

Middleware

method-override is a node package that allows us to catch incoming requests to the back-end and change the method from POST to DELETE or PUT. We'll use the method-override middleware that looks for a _method=DELETE or _method=PUT query string in the request URL and swap out the method accordingly.

By default, method-override will only override POST methods, because having a DELETE or PUT route accessible via a GET request "may introduce security issues and cause weird behavior when requests travel through caches"(see the options.methods section of the method-override docs for on this)

Setup:

1. Install method-override via npm.

2. Import the module

const methodOverride = require('method-override');

3. Configure middleware (make sure it lives above any other middleware code that uses the request method):

app.use(methodOverride('_method'));

DELETE

Delete should be used to delete an existing item. A delete request contains no payload (req.body) and no query string (req.query). The only data is expressed via a URL parameter which matches the item's name (req.params.name).

Since we can only use POST methods to activate the method-override functionality, we will use a form to submit the request. Let's start by adding a delete button (form submission) to our index page list items. Note that we must add a second forEach parameter in order to get access to the dinoId/index.

dinosaurs/index.ejs

<form method="GET" action="/dinosaurs">
  <label for="nameFilter">Filter by Name</label>
  <input id="nameFilter" type="text" name="nameFilter">
  <input type="submit">
</form>


<ul>
  <% myDinos.forEach((dino, index) => { %>
  <li><%= dino.name %> is a <%= dino.type %>
  
      <% let deleteRoute = "/dinosaurs/<%= index %>/?_method=DELETE"; %>
      <form method="POST" action="<%= deleteRoute %>">
          <input type="submit" value="Delete">
      </form>
      
  </li>
  <% }); %>
</ul>

index.js

app.delete('/dinosaurs/:idx', (req, res) => {
  const dinosaurs = fs.readFileSync('./dinosaurs.json');
  const dinoData = JSON.parse(dinosaurs);

  // remove the deleted dinosaur from the dinosaurs array
  dinoData.splice(req.params.idx, 1)

  // save the new dinosaurs to the data.json file
  fs.writeFileSync('./dinosaurs.json', JSON.stringify(dinoData));

  //redirect to the GET /dinosaurs route (index)
  res.redirect('/dinosaurs');
});

PUT

First we need a way for the user to edit an item. Add an edit link to the dinosaurs index.

/dinosaurs/index.ejs

<form method="GET" action="/dinosaurs">
  <label for="nameFilter">Filter by Name</label>
  <input id="nameFilter" type="text" name="nameFilter">
  <input type="submit">
</form>


<ul>
  <% myDinos.forEach((dino, index) => { %>
  <li><%= dino.name %> is a <%= dino.type %>
  
     <% let editDinosaurLink = "/dinosaurs/edit/<%= index %>"
      <a href="<%= editDinosaurLink %>">Edit</a>
      
      <% let deleteDinosaurAction = "/dinosaurs/<%= index %>/?_method=DELETE"
      <form method="POST" action="<%= deleteDinosaurAction %>">
          <input type="submit" value="Delete">
      </form>
  </li>
  <% }); %>
</ul>

Now we have to create a form for editting the information and submitting the PUT request.

/dinosaurs/edit.ejs

<form method="POST" action="/dinosaurs/<%=dinoId%>/?_method=PUT">
    <label>Name</label>
    <input type="text" name="name" value="<%= dino.name %>">
    <label>Type</label>
    <input type="text" name="type" value="<%= dino.type %>">
    <input type="submit">
</form>

We need a GET route to view this form!

index.js

app.get('/dinosaurs/edit/:idx', (req, res) => {
  const dinosaurs = fs.readFileSync('./dinosaurs.json');
  const dinoData = JSON.parse(dinosaurs);
  res.render('dinosaurs/edit', {dino: dinoData[req.params.idx], dinoId: req.params.idx});
});

Finally we can write our PUT route! The form submission will return the editted values through req.body, just like we saw with the new.ejs view and POST route. Now all we need to do is edit the JSON accordingly.

index.js

app.put('/dinosaurs/:idx', (req, res) => {
  const dinosaurs = fs.readFileSync('./dinosaurs.json');
  const dinoData = JSON.parse(dinosaurs);

  //re-assign the name and type fields of the dinosaur to be editted
  dinoData[req.params.idx].name = req.body.name;
  dinoData[req.params.idx].type = req.body.type;

   // save the editted dinosaurs to the data.json file
  fs.writeFileSync('./dinosaurs.json', JSON.stringify(dinoData));
  res.redirect('/dinosaurs');
});

Last updated