Dispatching Requests Through REST

At its most basic, a web application accepts an incoming request from a browser, processes it, and sends a response.

The first question that springs to mind is, how does the application know what to do with the incoming request? A shopping cart application will receive requests to display a catalog, add items to a cart, create an order, and so on. How does it route these requests to the appropriate code?

It turns out that Rails provides two ways to define how to route a request:a comprehensive way to use when we need to and a convenient way that we’ll generally use whenever we can:

  • The comprehensive way lets us define a direct mapping of URLs to actions based on pattern matching, requirements, and conditions.
  • The convenient way lets us define routes based on resources, such as the models we define. Because the convenient way is built on the comprehensive way, we can freely mix and match the two approaches.

In both cases, Rails encodes information in the request URL and uses a subsystem called Action Dispatch to determine what should be done with that request. The actual process is very flexible, but at the end of it, Rails has determined the name of the controller that handles this particular request along with a list of any other request parameters. In the process, either one of these additional parameters or the HTTP method itself is used to identify the action to be invoked in the target controller.

Rails routes support mapping between URLs and actions based on the contents of the URL and on the HTTP method used to invoke the request. We’ve seen how to do this on a URL-by-URL basis using anonymous or named routes. Rails also supports a higher-level way of creating groups of related routes. To understand the motivation for this, we need to take a little detour into the world of Representational State Transfer.

REST: representational state transfer

In a REST approach, servers communicate with clients using stateless connections. All the information about the state of the interaction between the two is encoded into the requests and responses between them. The long-term state is kept on the server as a set of identifiable resources.

Clients access these resources using a well-defined(and severely constrained)set of resource identifiers. In our context, these are URLs. REST distinguishes the content of resources from the presentation of that content. REST is designed to support highly scalable computing while requiring applications to be decoupled by nature.

There’s a lot of abstract stuff in this description. What does REST mean in practice?

First, the formalities of a RESTful approach mean that network designers know when and where they can cache responses to requests. This enables load to be pushed out through the network, increasing performance and resilience while reducing latency.

Second, the constraints imposed by REST can lead to applications that are easier to write and maintain. RESTful applications don’t worry about implementing remotely accessible services. Instead, they provide a regular, straightforward interface to a set of resources. Our application implements a way of listing, creating, editing, and deleting each resource, and our clients do the rest.

Let’s make this more concrete. In REST, we use a basic set of verbs to operate on a rich set of nouns. If we’re using HTTP, the verbs correspond to HTTP methods (GET, PUT, PATCH, POST, and DELETE, typically). The nouns are the resources in our application. We name those resources using URLs.

Dispatching requests in our application

The Depot application that we produced contained a set of products. There are implicitly two resources here. First, there are the individual products. Each constitutes a resource. There’s also a second resource in the form of the collection of products.

To fetch a list of all the products, we could issue an HTTP GET request against this collection, say on the path /products. To fetch the contents of an individual resource, we have to identify it. The Rails way would be to give its primary key value, that is, its ID). Again, we’d issue a GET request, this time against the URL /products/1.

To create a new product in our collection, we use an HTTP POST request directed at the /products path with the post data containing the product to add. Yes, that’s the same path we used to get a list of products. If we issue a GET to it, it responds with a list. If we do a POST to it, on the other hand, it adds a new product to the collection.

Let’s take this a step further. We already know how to retrieve the content of a product. We just issue a GET request against the path /products/1. To update that product, we’d issue an HTTP PUT request against the same URL And, to delete it, we could issue an HTTP DELETE request using the same URL.

Let’s take this even further. Maybe our system also tracks users. Again, we have a set of resources to deal with. REST tells us to use the same set of verbs (GET, POST, PATCH, PUT, and DELETE) against a similar-looking set of URLs (/users, /users/1, and so on).

Now we see some of the power of the constraints imposed by REST. We’re already familiar with the way Rails constrains us to structure our applications a certain way. Now the REST philosophy tells us how to structure the interface to our applications too. Suddenly our world gets a lot simpler.

Rails has direct support for this type of interface through a kind of macro-route facility called resources. Let’s take a look at how the config/routes.rb file might have looked back in Creating a Rails Application:

Depot::Application.routes.draw do 
  resources :products

The resources line caused seven new routes to be added to our application. Along the way, it assumed that the application will have a controller named ProductsController, containing seven actions with given names.

Take a look at the routes that were generated for us. We do this by making use of the handy rails routes command.

Get hands-on with 1200+ tech skills courses.