Bootcamp
Search…
3.1.4: PUT, DELETE Requests

Introduction

GET, POST, PUT, and DELETE are 4 of the most common HTTP methods on the Internet, corresponding to 4 core operations of most apps today: Create (POST), Retrieve (GET), Update (PUT), and Delete (DELETE), aka "CRUD". Most apps primarily consist of CRUD functionality, in contrast to apps that might involve more advanced technologies such as blockchain or machine learning.
Let's learn how to use PUT and DELETE requests in our apps.

PUT Requests

Let's use PUT requests to edit data we've already stored in our app. For data deletion, we will use DELETE requests as described in the DELETE section below.

Express Route

  1. 1.
    app.put works very similarly to app.get and app.post, accepting URL path and. callback params.
    app.put(<PATH>, <CALLBACK>);
  2. 2.
    The URL paths for PUT requests at Rocket typically include a path parameter that specifies the resource to edit, for example with an index value. We need this parameter to determine which data to edit.
  3. 3.
    In the below example we replace all data at the specified location with data from the request. We could also append request data to data at the specified location if we wanted.
    app.put('/recipe/:index', function (request, response) {
    const { index } = request.params;
    read('data.json', (err, data) => {
    // Replace the data in the object at the given index
    data['recipes'][index] = request.body;
    write('data.json', data, (err) => {
    response.send('Done!');
    });
    });
    });
  4. 4.
    Send a test PUT request with Curl or Thunder Client.
    curl -X PUT -d fruit=apple localhost:3004/recipe/1
  5. 5.
    Verify we updated the relevant recipe in data.json.

Method Override

  1. 1.
    ​HTML has yet to allow native generation of requests with methods other than GET and POST.
  2. 2.
    We still want the communication benefits of using PUT and DELETE requests, so we will use the official workaround to send PUT and DELETE requests.
  3. 3.
    The official workaround is to add a designated query parameter to the request URL to determine request method, and to bind a special middleware in Express that reads the designated request method query param and forwards the request to the relevant routing method.
  4. 4.
    Install the Method Override NPM package.
    npm install method-override
  5. 5.
    Add following code to import Method Override and bind the middleware to our Express app in index.js.
    import methodOverride from 'method-override';
    ​
    // Override POST requests with query param ?_method=PUT to be PUT requests
    app.use(methodOverride('_method'));
  6. 6.
    Add the designated query parameter to the end of the form action URL so that Express can detect this as a PUT request. The form should still have method "POST".
    /recipe/1?_method=PUT

PUT Form

  1. 1.
    We need a GET route to render the edit form that will create and send the request to our PUT route.
  2. 2.
    The path for GET routes to edit forms typically end with the suffix "edit".
    app.get('/recipe/:index/edit', (request, response) => {
    response.render('edit');
    });
  3. 3.
    The edit form might look like the following in an EJS template edit.ejs.
    <html>
    <body>
    <form action="/recipe/<%= recipe.index %>?_method=PUT" method="POST">
    <label for="label">recipe label:</label><br />
    <input type="text" id="label" name="label" /><br />
    <label for="ingredients">ingredients:</label><br />
    <input type="text" id="ingredients" name="ingredients" /><br />
    <input type="submit" value="Submit" />
    </form>
    </body>
    </html>
Form HTTP Method Remains POST Even When Sending PUT Requests
Note we use method="POST" in our form tag even though we want the form to send a PUT request. This is because HTML forms currently do not support PUT and DELETE requests natively. More info here.

Render Current Data in Edit Form

  1. 1.
    After verifying our edit form and route work, we can improve our user's experience by showing them the current data in the edit form so they know what they are editing and don't need to re-enter existing data.
  2. 2.
    To accomplish this, we might modify our GET route to read from our DB and render the edit form with the existing data. Notice we pass the recipe index to the EJS template so the form's PUT request URL has the correct target index.
    app.get('/recipe/:index/edit', (request, response) => {
    // Retrieve current recipe data and render it
    read('data.json', (err, jsonData) => {
    const { index } = request.params;
    const recipe = jsonData.recipes[index];
    // Pass the recipe index to the edit form for the PUT request URL.
    recipe.index = index;
    const ejsData = { recipe };
    response.render('edit', ejsData);
    });
    });
  3. 3.
    We use EJS to pre-populate our form with the current recipe data.
    <label for="label">Recipe Label</label><br />
    <input type="text" id="label" name="label" value="<%= recipe.label %>" /><br />
    <label for="ingredients">Ingredients</label><br />
    <input
    type="text"
    id="ingredients"
    name="ingredients"
    value="<%= recipe.ingredients %>"
    />

Request Flow

It takes 2 request-response cycles to edit data in our app.
  1. 1.
    GET request to render the HTML form to edit data.
  2. 2.
    PUT request sent by the form to edit data.
Our app uses 2 request-response cycles to edit data.
  1. 1.
    Render Form
    1. 1.
      The browser makes a GET request to render the form. The request includes the index of the recipe to edit.
    2. 2.
      Express extracts the relevant recipe's data from the DB and responds with the recipe's values in the form. The form's action attribute contains the recipe's index.
    3. 3.
      The browser renders the form with the recipe's current values in it.
  2. 2.
    Submit Form
    1. 1.
      The user clicks the form's submit button, sending a PUT request to /recipe/2 specified in the form's action attribute.
    2. 2.
      Express responds with a redirect GET request to /recipe/2. The diagram above does not show the subsequent GET request to view the edited recipe's data.

DELETE Requests

  1. 1.
    Our recipe app's delete functionality removes a recipe from the array of recipes in our DB. We use JS's splice method to remove a specific element from an array.
    app.delete('/recipe/:index', (request, response) => {
    // Remove element from DB at given index
    const { index } = request.params;
    read('data.json', (err, data) => {
    data['recipes'].splice(index, 1);
    write('data.json', data, (err) => {
    response.send('Done!');
    });
    });
    });
  2. 2.
    The data index is the only information we need to delete data, thus we typically don't render delete forms on their own pages. Delete forms typically exist on pages that show the data that can be deleted, for example / or /recipe/:index pages in our recipe app example.
  3. 3.
    The delete form may only contain a single button that triggers a DELETE request on the data with the specified index. Note it uses the Method Override parameter like we did with PUT requests above.
    <form action="/recipe/<%= index %>?_method=DELETE" method="POST">
    <input type="submit" value="Delete" />
    </form>