“CRUD is not REST”:http://nicksda.apotomo.de/2010/10/rails-misapprehensions-crud-is-not-rest/ is what I was trying to clarify in my last restful post. There surely is a terrible confusion in the community about that.
* *CRUD is implementation* where you simply could pull in some module into your controller and get a CRUD interface for free.
* *REST is an implementational approach* – there simply is no library that after inclusion makes your code _”restful”_. There are great gems to give you CRUD code and make it available as a REST interface. However, the real work like *deciding which and how resources are exposed, is up to you*, the software architect.
*REST is more than HTTP restrictions*. That’s what I found after studying all the great comments in the last post. Thanks for the helpful links and explanations, guys!
This *really is just the tip of the iceberg*, “as Juan stated correctly”:http://nicksda.apotomo.de/2010/10/rails-misapprehensions-crud-is-not-rest/#comment-645.
h3. HTTP restrictions?
With CRUD in mind, REST initially seemed to be nothing more than a constraint saying: _Put your verbs from the URL to the HTTP header._
Instead of having a URL like @/users/new@ I simply send a POST to the @/users/@ resource. Being _”RESTful”_, *a POST is defined to create a new resource*.
So, great, we got GET, POST, PUT and DELETE that are – more or less – *semantically mapped to CRUD operations*.
h3. RESTful urls
Browsing some Rails discussion about REST you can often *find the term _”RESTful url”_*, which usually refers to something like
Following “Adam’s explanation”:http://nicksda.apotomo.de/?p=465&preview=true#comment-646 this has nothing to do with REST – *it’s a friendly URL, nothing more!*
So the real point about URLs in REST is: They are *network addresses pointing to a unique resource.* If it is pretty, so much the better! Thanks to Rails we got friendly URLs, but keep in mind that this alone doesn’t make ’em RESTful.
Let’s look at the term _resource_ to find out how real REST urls look like.
h3. What are resources?
In CRUD context I said that *resources are _usually_ mapped to business objects*, like users, comments or any other model with a _database table_.
_Usually_ here means: *Resources could (and should!) be just anything.* They don’t need to map to distinct tables. *Transient resources – like a blog feed aggregating posts – are the crucial point about REST*. That’s where we have to act as architects deciding what to expose.
Taking the feed as example *a real resource identification* would be
A *REST id is always a complete URI* with host, path, etc.
So on the one hand *we have primary keys identifying model instances, and on the other we have REST ids*. It’s easy to confuse that!
In a _RESTful environment_ you precisely shouldn’t talk about _”user 4711″_ but about the ressource @http://example.com/users/4711@. *REST is about hiding implementation details*, why, this being the case, are we talking about primary keys?
REST usually operates via HTTP, which doesn’t mean that resource controllers need to render HTML only.
Rails 3 already does a pretty good job here. Controllers and models have *built-in renderers for different representations of resources*.
class Feeds < ApplicationController respond_to :html, :xml, :csv def index @feeds = Feed.all respond_with(@feeds) end
When requesting an XML list of available feeds *the controller will detect that, delegate to the model* and returns an XML document without having written any user code at all.
Anyway, what’s still missing here is *content negotiation*.
h3. Content negotiation
Let’s take the ruby way to illustrate that problem. I use “restfulie”:https://github.com/caelum/restfulie to write a quick REST client.
Restfulie.at("http://localhost:3000/feeds/"). accepts("application/xml"). get!
Restfulie is a wonderful gem. This code will retrieve my feeds in XML. Here’s my server output.
Started GET "/feeds/" for 127.0.0.1 at 2010-12-... Processing by FeedsController#index as XML
Obviously, this worked.
Now, *content negotiation implies that we can request a list of acceptable representations*.
What if I say _”I want your feeds in ATOM first. If you don’t speak ATOM, gimme XML!”_.
Restfulie.at("http://localhost:3000/feeds/"). accepts("application/atom"). accepts("application/xml"). get!
Restfulie takes care of the content negotiation, which is pretty awesome. Information about *which formats to send is handled in the @Accept:@ header flag*.
Started GET "/feeds/" for 127.0.0.1 at 2010-12-... Processing by FeedsController#index as APPLICATION/ATOM Completed 406 Not Acceptable
Well, I’d say *the content negotiation failed*, although we do provide XML!
What happened? Rails didn’t look at the second accepted format. This usually is not a problem, as most people don’t use content negotiation. *Restfulie provides a convenient module to enable your controller of real content negotiation*. I’ll post about that separately.
The reason for my lack of understanding here is that Rails simply doesn’t support it, yet. This isn’t a severe bug, *I just want to point out what else is in REST*.
A couple of comments “remarked another principle”:http://nicksda.apotomo.de/2010/10/rails-misapprehensions-crud-is-not-rest/#comment-644 in REST that is often swept under the table: *Hypermedia support.*
*When I hear _hypermedia_ I think of JPEG images and links*. Not sure why.
It first seemed strange to embedd workflow-related links into my resource representation. This basically means *the resource contains links to other “application parts“* where browsing to would make sense in the current application state.
Consider we were looking at a XML representation of a feed – *while it contains a lot of post snippets, it could also contain a link to actually _subscribe_*.
The feed list *_proposes_ what to do from here*. Isn’t that great?
While it is the client’s job to extract that links and decide which action to display, *the client as-it is not responsible for generating these links*. The resource provides us links to change the application state – *this is called HATEOAS*.
While I am still exploring this field of REST I just started realizing how *HATEOAS can help building forward-compatible, modular applications*.
Did I really manage to work out that REST is more than GET, POST, PUT and DELETE?
So, REST has five basic concepts, that are
* *Unique resource identification* which is sometimes mistaken as _RESTful url_
* *Standard interface* for resources like GET, POST and friends. Each request type has its own semantic and may or may not alter the resource.
* *Representations* and content negotiation to display resources in different apparels.
* *Hypermedia* to embedd further actions into the resource.
* and *stateless communication* – why not write about that separately?
11 thoughts on “Rails Misapprehensions: REST is more than GET, POST, PUT and DELETE!”
Nice summary. Another thing that might be worth some attention is the fact that content-negotiation can also help with versioning, provided you use application-specific MIME types (rather than just “application/json” or whatever). An example of how to do this is described here:
(I prefer using the version parameter in the MIME type, but the basic idea is the same …)
I really like your Rails Misapprehensions series 😉
Speaking about REST I would add one more keyword: API. Using REST gives one nothing less then very clean interface to deal with exposed resources.
Appreciate the insight of whats beyond the HTTP verbs. However I didn’t quite get your gripe with the RESTful URLs. If the definition is:
“They are network addresses pointing to a unique resource.”
Then what’s not RESTful about:
It looks to me as an address pointing to a unique resource. Which would mean it could be validly said to be restful, no?
@Dan: Thanks for the link! That’s something I could need myself soon. Great!
@Michał: Yeah, right, API! I thought I discussed this in the last article 😉 Planning to write another post about this. Nastrovje!
@David: Good question, two things here.
1. RESTful URLs are fully-qualified URIs (with host and stuff)
2. I don’t say that /users/.. is not RESTful. If it is pointing to a unique resource, it is! However, in Rails people tend to say /users/4711 is “restful” whereas something like /users?id=4711 is “not restful”, which is wrong. For me it was confusing when everybody was speaking of “RESTful” urls even when they meant pretty urls.
Any thoughts on how to best do HATEOAS in Rails with API type responses? I blogged about my dilemmas here: http://goo.gl/Xs7Kg
Michael, you are right. Once a REST api is used for several web services, a client should be able to use the SAME client api to access all of them. That means just one jar or gem to access, navigate, parse, iteract with all rest systems at once. That’s why a “Mywebsite REST jar/gem” does not fit well with the rest way of thinking.
Great post Nick. I would even add code on demand at the very bottom, although it is still quite controversial on the machine 2 machine web (except for mobile)
Nick, I’m glad I could be helpful.
A couple thoughts… just to make your life miserable:
1) I don’t think its _necessarily_ wrong to use absolute paths rather than the fully qualified URL, or even relative URLs any more than it is in an HTML document, you simply need to specify rules about how to resolve those URLs. If we use David’s example, If I’m looking at the representation for /users/4711/ then it would be reasonable to have a link of “comments/9”, provided there was adequate semantics to resolve that.
The issue is that in rails you would send some json like:
and expect the client to reconstruct the url.
2) The issue of API is critical. It is RARE that an API will result in something that is usable for real people. If you are creating an HTML UI for people, then a REST API is not typically going to meet their needs, or you’re going to find all sorts of awkward work arounds. Build a User Centered UI for human facing interfaces and a REST API for system facing interfaces.
HTML browsers are not REST clients. They cannot issue PUT or DELETE commands. RAILs uses all sorts of trixy work arounds to make it work (via a hidden action field), but this proves that the style does not apply to Web1.0 style interfaces.
Before you yell at me… the XHR object is a restful client so you can create RESTful client applications hosted in HTML browsers, but that is NOT the same thing.
3) Think about the implications of using the fully qualified domain name in your representations. Why keep everything on the same application? Have a single authentication and authorization application and use URIs on that application to identify users (or better use use something from OAuth or OpenID). Separation of Concerns and decoupling are key goals in system design and using REST you could separate each concern into its own application coupled ONLY by URIs and the mediatypes supported by each application. Also remember you’re not limited to using one URI scheme, so the OAuth example above might have an oauth: scheme rather than an http: scheme so that the connection can be handled correctly. The opportunities are massive.
The key bit of HATEOAS is the engine of state.
I have been thinking about Entity Lifecycles lately, and I’m not sure that there is much already done in the world of Rails about this.
I would imagine that a State mixin to the model should provide the URI’s for next state transitions.
Take a Shipment entity, it could have ‘created’,’part_shipped’,’drop_shipped’,’fully_shipped’,’part_received’,’fully_received’,’completed’ states. A Shipment entity in the ‘created’ state could provide further action uris to move the entity to ‘part_shipped’,’drop_shipped’ etc. but not ‘*_received’ or ‘completed’.
@Guy: We are working on a new REST layer for Rails using Restfulie. In the current design, the controllers push HATEOAS links into the models, since they know best about URLS, actions and other controllers.
I will blog about that soon. Thanks for that valuable comment, dude! The FSM approach in the model is quite awesome, it could in turn query the controller for the respective URLs! Cool!
@Nick: Granted. The mixin encapsulates the differences between a business object and the model/persistence object and would include the logic to manage the business entities lifecycle.
the social network Piczo.com used a number of verbs in it’s piczo zone because it was much more dynamic than a crud application. publish, consume, rate, comment, view, browse, search were just a few of these verbs.
if you had a site that catered to teens and not developers or one that isn’t a crud interface, why would you use GET, POST, PUT and DELETE for your rest implementation?