Before we talk about whatever this "REST" thing is, let's revisit how a web request travels from the end-user's web browser, through the maze of our application, and out the other side:

  • The end-user hits a path, like /tacos, in our application – this request is made possible by the routes file, which lets our application know that we want to define a resource for tacos
  • Because we have a resource called tacos, we need to have a tacos controller. And, in that controller, we've had an index action.
  • The code in the action creates and populates variables needed by the index view, which is essentially HTML with a few dynamic bits where we're displaying the contents of those variables. Plain ol' vanilla HTML is then generated and sent back as the response to the user's web browser.

It's time to peel another layer of the onion away, and go more in-depth on the topic of actions. So far, we've seen the index action, and that it's simply a method in our controller class defined by def index... end – but what is an action and what is it for, exactly? To understand this, we've got to go for a ride through some new concepts, so hang on tight.

A Web App == Things to CRUD

In our initial discussion about Ruby on Rails (in Server-Side Rendering Using Ruby on Rails), we talked about how the primary function of the Rails framework is to allow us to build applications that are a collection of things to CRUD (Create-Read-Update-Delete). We talked about how important that idea is, and to hang onto that thought for later... well, later is now!

If we really think about it, indeed, almost all applications are really a collection of things we want to CRUD in our application's database. A CRM application is where we can CRUD accounts, contacts, sales activities, etc. A photo-sharing social media application, a la Tacostagram, is where we CRUD posts, likes, comments, etc. An e-commerce platform allows for the CRUD of products, reviews, orders, shipments, etc. The web application simply provides a front-end to allow users to perform all those actions in the database.

That's a pretty straightforward concept; however, users can't write SQL directly in our application! Rather, a user fills out a form or clicks a button in our app – how does doing these things translate to say, a SQL SELECT or INSERT or UPDATE statement in the database?

The answer is REST.

REST

What is REST? It stands for Representational State Transfer 🥱 – but nevermind that. All we need to know is that it's yet another design pattern! And it's one that's designed to do precisely the job described above – to map user actions in web applications to data. More specifically, it gives us a proven method to map things a user can do in the web browser to the CRUD capabilities of SQL.

The web (HTTP) + databases (CRUD) = REST.

HTTP

We have to learn one more critical thing about web browsers to complete the story of REST. We already seen how web browsers make a request to a web server, and expect a HTML response in return. In fact, you're probably sick of hearing about it by now! What we haven't looked at, however, is that web browsers can send (and web servers can accept) different types of requests.

When we type https://SomeWebSite.com/ into the web browser's address bar, we are sending a GET request – GET requests are the type of request we've seen thus far. The web address, or URL, is plainly exposed by the web browser, via the address bar. We either type the URL into the address bar ourselves, or we access them via a link on the page we're using – either way, these are GET requests.

POST, PATCH, and DELETE requests occur underneath the hood of the web browser. An end-user can't force a web browser to make these types of requests – only the web developer can, and it's typically built to do so when end-users submit forms. End-users regularly make these types requests without knowing. For example, the only time we're made aware of POST requests are when we try to refresh a page that's the result of a POST request, and we get that "are you sure you want to POST this form again" message that we've likely seen before.

Types of requests – GET, POST, PATCH, DELETE – are known as HTTP Methods (or Verbs) – the actions that web browsers can take. Unlike the "nouns" of the web (aka our resources), of which we can have an infinite amount, there are only 4 HTTP Methods (Verbs) in all:

HTTP Method Usage CRUD
GET Visiting a web page Read
POST Submitting a form that creates new data Create
PATCH Submitting a form that updates existing data Update
DELETE Submitting a form that deletes existing data Delete

And, as we can see, each HTTP Method is intended to map to a different part of CRUD we're doing in the database. This is REST! REST is simply a way to design our applications such that we have nouns (aka resources) – as many as we want – but each resource has only four verbs (aka HTTP Methods) that can be performed.

Implementing REST in Rails

Now that we understand the underlying theory of REST, let's put it to work in Rails.

In Rails, we've already seen how resources (the nouns of REST) are implemented – there is a line in the routes file that tells the application the resource exists, and there is a controller for the resource. And each of the resource (noun) and HTTP method (verb) combinations result in – you guessed it – an action.

Let's start with 2 of the GET actions – the ones where a user reads data about a particular resource. In practice, this is implemented with two actions – one where we're reading a collection of resources and one where we're reading a single resource. For example:

  • A page that's a list of all books in our store, and a page about a single book.
  • A page that's the list of all courses in our school, and a page that's about a single course.
  • A page that's the list of all accounts in our CRM, and a page that's about a single account.

We've already been introduced to the page that's a list of all records – this is our old friend, the index action. When we go to /books or /tacos or /dice or /cards – that hits the index method of the resource's controller, and subsequently displays the index.html.erb view of that resource. For example, in our CRM application, we might want to have a list of all companies. Naturally, this would be the companies resource. The complete implementation might look something like this:

resources :companies
In the routes file – config/routes.rb
def index
  @companies = Company.all
end
In app/controllers/companies_controller.rb
<h1>My CRM</h1>
 
 <ul>
   <% for company in @companies %>
    <li>
      <%= company["name"] %>
    </li>
   <% end %>
</ul>
In app/views/companies/index.html.erb

Not much different than what we did last time (in Data in Views), aside from the fact that we're now properly following the MVC data pattern. Also, note the the action name – index – corresponds to the filename of the view – index.html.erb. That's good enough for our page of companies – our index action – now, let's create the page for a single company.

The show Action

The page for a single company is known as the show action. Since this is a page for a single resource, we need to know which resource we want. And, we know that every resource (e.g. every company) is identified in the database by an auto-incrementing primary key ID. So we have to supply that ID in the GET request – in other words, by making it part of the URL path.

So, if the index action is the one that displays all companies, and the HTTP method and path is:

GET /companies

Then, the HTTP method and path for a single company is:

GET /companies/123

... where 123 can be replaced with whatever the ID is of a record in the database. Let's write the controller code for our show action next:

def show
  @company = Company.find_by({ "id" => params["id"] })
end

There are a couple of new things happening here:

  • We're using the find_by method of an ActiveRecord object. This method returns exactly one object corresponding to the record that has that id.
  • params["id"] – this is what contains the "123" part of the URL, which is the ID of the record in question

Then, much like the index action, we add a view that contains the HTML markup for however we'd like to present the single resource – a single company, in this case – to the user. The name of the view should match the name of the action, followed by .html.erb, so for our show action, that's show.html.erb:

<h1><%= @company["name"] %></h1>
<h2><%= @company["city"] %>, <%= @company["state"] %></h2>
app/views/companies/show.html.erb

Now we have a page that shows a company and all of that company's pertinent information. Lastly, for the sake of completeness, let's link the index and show pages together, by editing the index.html.erb view:

<h1>My CRM</h1>
 
<ul>
  <% for company in @companies %>
    <li>
      <a href="/companies/<%= company["id"] %>"><%= company["name"] %></a>
    </li>
  <% end %>
</ul>
app/views/companies/index.html.erb

Now, when we click on a company's name in the list of companies, we see the detailed page for that company – a (sort of) complete system, from a read-only perspective!

The 7 Actions

It turns out that there are 7 actions in all. This can be counter-intuitive, since there are 4 letters in CRUD, and 4 HTTP methods. But there are extra actions needed to support it all. Here is a full list (assuming "tacos" are the resource in question):

Action Use Case HTTP Method Path
index Read (display) a list of all tacos GET /tacos
show Read (display) information on a single taco GET /tacos/123
new A form to create a new taco GET /tacos/new
create Receives the information from the "new" form and creates the new taco POST /tacos
edit A form to update an existing taco GET /tacos/123/edit
update Received the information from the "edit" form and updates the existing taco PATCH /tacos/123
destroy Processes a request to destroy an existing taco DELETE /tacos/123

Key points:

  • There are multiple (4) GETs. Each of these 4 actions is requested by its URL path and can be linked to/bookmarked
  • There are 2 pairs of actions that are intended to work together, namely new/create and edit/update – one is the form, and the other receives the form data
  • There are duplicate URL paths – i.e. /tacos and /tacos/123 – but notice that the HTTP methods are different. There is no duplication of the combination of URL path and HTTP method.
  • Not all actions have views! In fact, typically only the GET requests do. The other actions simply accept parameters, do something with them (like create or update a record in the database), and send the user on their way.

In the next lessons, we'll be looking in-depth into the remaining (new/create, edit/update, and destroy) actions.