How to make a DELETE form using Rails Middleware in 3 easy steps
Tags: webdev, rails, html, javascript
Published: 2020-05-22 19:43:37

If you’ve recently learned how to create forms for GET and POST methods, your next inclination is probably to try out DELETE or PATCH. However, you’ll quickly find out that forms do not support these and that no one knows exactly why this is (if you happen to know, send me a message!).
 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jbvd9yq090hkou8ltsb5.png)

The internet is filled with vague work arounds and half-formed tutorials for making a delete form. It will make you wonder how the heck anyone else figured this out. Turns out, as per usual with programming, there are multiple ways to accomplish this goal. This tutorial will show you one of them so you don’t have to patch together (no pun intended) various ambiguous instructions from multiple websites. 

###**Behold, Rails Middleware...**

**What is Middleware, in beginner’s terms?** 
Essentially it is a separation of concerns. Middleware includes a bunch of methods that an HTTP request is passed through before it finally hits your routes and controller methods. Some of this is automatic, and other times, you need to manually enable a certain middleware method. 

**What does this have to do with making a form DELETE rather than GET or POST?**
Well, if middleware can influence an HTTP request before it hits your controller, then maybe there’s a middleware method that can change your request from GET/POST to DELETE!

And guess what, there is! It's called **Rack::MethodOverride**. This magical thing is very obscurely documented, so I’m going to lead you step-by-step on how to get it working. You can also see the code directly on my Github (README tells you how to set it up quickly): [Backend repo](https://github.com/wolf-dominion/sampleAPI_delete_form_tutorial_backend) | [Frontend repo](https://github.com/wolf-dominion/sampleAPI_delete_form_tutorial_frontend)

###**Overview of what to do:** 

1. Enable Rack::MethodOverride in your backend
2. Create a form in your HTML file
3. Customize the form in your Javascript file

___
<center>**Step 1: Enable Rack::MethodOverride in your backend**</center> 

1. In your Rails application, navigate to ‘config/application.rb’
2. Within this file, navigate to the module that has the same label as your application, in my case, SampleApi 
3. Under “config.load_defaults 6.0”, paste in: “config.middleware.use Rack::MethodOverride” and save. (For the sake of readability, I’ve removed the default comments)
```javascript
module SampleApi
  class Application < Rails::Application

    config.load_defaults 6.0
    config.middleware.use Rack::MethodOverride

    config.api_only = true
  end
end
```
___
<center>**Step 2: Create a form in your HTML file**</center>

1. In your HTML file when creating the form, leave the action empty, and set the method to “POST”
2. Make an input element inside the form with these attributes:
 
- type=”hidden”, meaning the user of your webpage will not see it
- name=”_method”, this is for the backend to recognize that this input is actually to be taken as a method
- value=”delete”, this sets the previously mentioned method to DELETE rather than POST

```html
    <form action="" method="POST">
        <input type="hidden" name="_method" value="delete" />
        <label>Select book you want to remove from your list:</label>
        <p></p>
        <select id="book-select" name="book_id"></select>
        <input type="submit" value="Remove book from your list">
    </form>
```
___
<center>**Step 3: Customize the form in your Javascript file**</center>
1. Grab the value in the input field that you want to send to the backend, in the case of DELETE, you’ll want the id of the object you want to delete. In my example, my function is getSelectionValueOnDeleteForm():```javascript
   	// grabs the initial value when the page loads (in this case, the "Coraline" book)
    	let select_id = document.getElementById("book-select");
    	let id_value = select_id.options[select_id.selectedIndex].value;

    	// grabs the value of the menu drop-down selection when the user clicks on a book, 
    	// but hasn’t pressed submit yet
    	document.getElementById('book-select').addEventListener('change', function() {
        	id_value = this.value;
    	});
    	return id_value;
```

2. Finally, grab the form element and set its action (remember how we left this blank in the HTML file?):```javascript
function setActionTypeOnDeleteForm(){
    	id_value = getSelectionValueOnDeleteForm()
   	    const bookDeleteForm = document.querySelector('body > form')
   	     id_value = parseInt(id_value)
   	     bookDeleteForm.action = `http://localhost:3000/books/${id_value}`
}
```

3. Now that these steps are complete, in one terminal from your backend application, run “rails s”. And in another terminal or tab, from your front end folder, run “lite-server”. Watch the magic happen. Or start bug fixing, because if you’re like me, there’s probably a typo or two in there.

**Important note**: If you already had your rails server running and lite-server, you need to restart them in order for the changes to take effect! And don’t forget to look over the README if you’re trying out my example code on Github. If you have any troubles, leave a comment and I'll do my best to help! 

**Resources**: 

The best articles I can find on Middleware: https://www.rubypigeon.com/posts/examining-internals-of-rails-request-response-cycle/

https://dev.to/xngwng/what-is-http-middleware-best-practices-for-building-desiging-and-using-middleware-5g10

Official guide: 
https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack