REST: Theory to practice

May 24, 2007 Pivotal Labs

REST. What is it, and how can it be used to design better web applications?

A presentation at RailsConf did me a great service by first pointing out all the things REST is not. It isn’t CRUD. It isn’t pretty URLs. It is neither a protocol nor an architecture, but it can play a role in your implementation of all of the above. REST itself though, is less concrete than all of that. It is a theoretical framework, a way of thinking about designing distributed software systems. For me, the first step in absorbing its principles is to forget about the database and focus on the fundamentals. This article will start there, then drill down to show how these ideas can help organize the development of your Rails applications.

Noon: Rest From Work (After Millet) by Van Gogh

(Noon: Rest From Work (After Millet) by Vincent Van Gogh)

REST encourages a focus on resources. A resource is anything that can be named, and your system can have as many resources and corresponding names as you want. Conversely, there is a limited set of operations defined on those resources. Unlike objects in object-oriented programming languages, which support very diverse, rich interfaces, resources in a RESTful system are relatively uniform. So how can a sophisticated API be developed if REST requires a fixed and limited number of operations that resources can support? The answer: add more resources!

Lets play with an example. Say I’m writing a web application that has a collection of shapes that can be moved to new locations. In an object oriented program, I might have a move method defined on each shape, but in this RESTful API, I have assigned consistent semantics to a limited set of operations supported by the HTTP protocol. I can show GET, create POST, update PUT, and destroy DELETE my resources. Nowhere in the HTTP specification is the a MOVESHAPE method defined. A naiive approach to remedying this limitation is to shoehorn this operation into the protocol by abusing URLs.

http://againstgrain.com/shapes/move/1

I call this abuse because URL stands for uniform resource locator, and it’s very hard to see this imperative-style command encoding as much of a resource. This is an API decision that fights the nature of the protocol it uses. How can the API go with the flow?

There are many potential solutions. Let me outline two, the first very simple, the second more sophisticated.

The first solution is to recognize that a move operation is just a change to the location of an object. If we expose this location as a resource and allow it to be updated, we’ll implement movement in a natural way without contorting URLs to name things that aren’t actually resources:

So we combine one of the four standard operations:
Update, represented by an HTTP PUT

With a resource:
http://withgrain.com/shapes/1/location

Updating the location of the shape will naturally equate to moving it.

But what if we want the movement to be relative to the objects current position, so that the client can say that they want a shape to move 5 pixels up and 10 pixels to the right without needing to know the objects current position or do any computation? To solve that problem, we apply a technique I learned doing computational semantics: reification.

Reification means that we give solid form or objecthood to something formerly fleeting or ephemeral. Anything can be reified. The fact that I am named Nathan Sobo can be thought of as my NathanSoboness, which is an (albeit abstract) conceptual object. Here we’ll apply the technique in a more conservative fashion, and say that shapes are associated with a history of movements. This movement history is a collection, which is itself a resource.

http://withgrain.com/shapes/1/relative_movements

Now say we want to move the shape. We combine the above resource locator with one of our standard operations, create, implemented as an HTTP POST. By posting a new movement to a shapes history, we cause the shape to move.

Now we’re working with HTTP rather than around it.

So how does this transfer to the design of Rails applications? Embracing resource oriented application development means you’ll be writing more controllers with fewer, more consistent methods. Lets work through a potential implementation of the shape API in Rails. It will all start in the routes file, with map.resources

Lets say we want to expose both the relative and absolute means of moving a shape. First we’ll start with a shapes resource.

map.resources :shapes

This represents the collection of all shapes in our system. It assumes the existince of a corresponding ShapesController and will set up a series of routes and url-generating methods to help reference the actions therein. Note the controller and its standard complement of methods below.

class ShapesController < ActionController::Base
  def index
  end

  def show
  end

  def create
  end

  def edit
  end

  def update
  end

  def destroy
  end
end

But with map.resources, the actions on the controller do not play a critical role in the url. They merely name the operations to which a given pairing of HTTP request method and URL will map. GETting /shapes will execute index. POSTing to /shapes will execute create. GETting /shapes/:id will execute show. PUTting to /shapes/:id will execute update, and DELETEing /shapes/:id will execute destroy. So even though there are 5 actions, there are really only two url patterns, one referencing the resource that is the collection of all shapes, and another referencing resources that are members of that collection. We can reference these URLs with automatically defined methods:

shapes_url ---> /shapes
shape_url(@square) or shape_url(@square.id) ---> /shapes/1

By pairing these with the correct HTTP method, we can access every operation we need.

Now lets add relative movement:

map.resources :shapes do |shape|
  shape.resources :relative_movements, :name_prefix => 'shape_'
end

class RelativeMovementController < ActionController::Base
  ...
end

This again assumes the existence of a RelativeMovementController with all of the standard methods defined on it. Except the resources supported by this controller are nested within shape resources, so the URL patterns look like this:

/shapes/1/relative_movements
/shapes/1/relative_movements/4

Because of the :name_prefix supplied (which will no longer be needed at some release of Rails in the future), we can refer to these URLs with helper methods that look like this:

shape_relative_movements_url(@triangle) ---> /shapes/1/relative_movements
shape_relative_movement_url(@triangle, 4) ---> /shapes/1/relative_movements/4

All of the same rules about HTTP method choice allow access to the RelativeMovementController‘s actions.

Now a cool twist: Singleton resources. Lets add the nested position resource to shapes.

map.resources :shapes do |shape|
  shape.resources :relative_movements, :name_prefix => 'shape_'
  shape.resource :position, :name_prefix => 'shape_'
end

And a corresponding controller, this time with a different complement of methods:

class PositionController < ActionController::Base
  def show
  end

  def edit
  end

  def update
  end
end

Because position is a singleton resource nested inside of shape, this controller is designed to deal with a single resource rather than a collection of them, so there is no need for an index action. The HTTP verb / URL combination mappings are also different. So PUTting to /shapes/1/position will invoke the update action.

None of these changes are Earth-shattering, but the simple act of focusing on resources is a force that will organize your application. Rather than growing a hodgepodge of actions on ever fattening controllers, you’ll instead create a greater number of controllers that are more circumscribed in their responsibilities.

What does this say about your model? Not much. I used to think that it was important to have a controller for every model object, and I no longer do. Controllers are responsible for supporting the exposure of resources to a remote API. This collection of resources is, in a sense, your remote client’s model. Whether your resources map precisely onto your underlying data model is your business. For example, you might expose resources that have no direct correspondence in the model layer. Or you might have model objects that you don’t choose to expose as resources.

But regardless, REST finally provides an organizing principle for the controller layer. Even if you don’t plan on exposing a RESTful API as a service, thinking in terms of resources will help you build more consistent applications and help you make fewer decisions.

I realize that this article has by no means covered every aspect of REST, but I hope it fills a gap that I felt as I was learning all of this.

About the Author

Biography

Previous
Rails Filter Parameter Logging
Rails Filter Parameter Logging

Q: How do you keep passwords from appearing in plain text in your Rails log file? A: Filter Parameter Logg...

Next
Don’t Underestimate the Power of Laughter
Don’t Underestimate the Power of Laughter

Do you laugh everyday at your job? If not, why? I think too many people become complacent at their job and...