REST
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:
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 thatid
. 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
:
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:
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.