whither CRUD?
Such drama in Rails-land !
the tangly roots of CRUD
Think about the beginning of the web. Berners-Lee’s original idea was that the web would be a readable and writable space from within the browser. Because of this, we have the GET, PUT, and DELETE HTTP methods, as well as POST, which is slightly less straightforward.
Since Hansson’s address at Railsconf, these have been widely noted as rough equivalents to the create, read, update, delete object manipulation paradigm.
However, it is useful to notice that create is just as legitimately mapped to PUT as it is to POST, depending on circumstances. In RFC 2616, PUT is described as creating or updating a resource at the given URI, while POST is described as creating a resource at a subordinate URI, if a resource is created at all. That is, if you already know what you want the identifier to be (for example, a permalink) for the object you want to create, a PUT-based create is more appropriate than a POST-based one.
CRUD does not span multiple models
In Rails, though, because practically every database record requires an RDBMS-generated non-semantic primary key, PUT creates are ruled out of hand, at least if the organization of the record’s object model is equivalent to the organization of the record as expressed in the user’s view. So basically, Rails forces creates to be POSTs, but the operation remains constrained within a single object model.
It is very important to notice something here: no method of CRUD spans multiple models.
CRUD, HTTP, and relational databases all operate similar to a filesystem. The client requests a resource, performs some transformation on it, and returns some resource (possibly the same resource) to the server. The server itself does not perform transformations on the resource, not ever ever. (WebDAV is the more fully realized extension of the HTTP-as-filesystem approach.)
search is not a resource
But if the server never updates the resource on its own, what’s the point of a controller, besides authentication? Well, there isn’t one, really. You can get away with this constrained method of interaction in many consumer web apps, because however complicated your data model, your application may still be basically views on the database.
But sometimes, the server will want to perform some transformations on existing data based on conditions supplied by the user. This is where CRUD breaks down. Search is an example of this. Search is not a resource, it is an operation.
no meta-operations in CRUD
There is no “perform operation X on resource Y” method in CRUD, or in a database, or in a filesystem. There is in HTTP, but it is free-form: it is POST. POST does not give you any help as to how to specify which operation, which resource, or which conditions. The web-as-read/write model, by design, does not take into account server-side operations on resources; after all, that’s up to the server.
So if you really want to follow CRUD exactly, even if you don’t span multiple models, all operations on a resource, instead of about a resource, must take place on the client side.
what would CRUD search look like?
There are many good reasons to instantiate your searches as objects, but trying to conform to CRUD is not one of them. A true CRUD search would be for the client to request every resource, and then look through them on its own. This avoids server-side manipulation of the data.
Searches can not be treated as simple reads in most domains, even if the content of the read is generated on the fly, because many searches are relative to the client (think Google personalized search). A URI for a single resource should not return non-same-identity result contents depending on who the requester is. Maybe multiple levels of access, perhaps, but not entirely different resources.
Even if you instantiate your search requests, you also need to receive back search results. If search results are part of the request object, then under CRUD the client shouldn’t have had to ask for them in the first place—it should have known them already. After all, no server-side operations on data. But if search results are a separate model, then how, under CRUD, can the client request them? Under CRUD/HTTP, the client is disallowed from creating an object of type A and receiving as a response an object of type B. There is no clear way (in my mind, at least) to guarantee that such a method would be idempotent, and if it takes place over PUT, which it might, it must be.
So, the client would have to know the URI that the requests will appear at. But if it knows that already, why did it have to submit a request in the first place? Forcing the client to wait around for some unspecified delay and telling it to hope that the results will have magically appeared at the target URI by then is not really an acceptable solution, programmatically.
other things inappropriate for CRUD
I think part of the confusion over this is related to Rails’ target domain. As mentioned above (and in some of the comments on Coda’s entry), consumer web apps do not need many server-side operations, whether on a single resource, or across multiple resources. It is easy to think of exceptions, though. Consider a Flickr-like application that allows the client to crop an image or adjust contrast. Should the client be forced to read the image, perform the cpu-intensive operation client-side (in Javascript most likely), and then update it back to the server? No, of course not. But should the client be required to create a “crop object” and submit that to the server, where from then on (unless the client deletes it) that particular crop will be accessible from a never-changing global URI? No, that’s ridiculous. Crop, like search, is an operation, not a resource.
conclusion
In my bioinformatics projects, the most frequent tasks are transformations of extant records into other types of records. In fact, the client never creates new records on its own, instead only performs operations on old records. The results of those operations, though, may become new records. CRUD is hardly ever appropriate for this situation.
I think, too, that part of my distrust of CRUD is related to my distrust of the command pattern. For me, at least, implementing and using the command pattern is rarely worth the trouble, because it can’t cleanly handle operations spanning multiple classes.
That’s all.
August 20, 2007
7 comments
I’m glad you posted about the fuzzy match between PUT and POST, and CRUD. I’d been thinking about it but the filesystem metaphor really nails it.
One thing I’m not sure about:
“A URI for a single resource should not return non-same-identity result contents depending on who the requester is.”
Even flickr.com is different depending on which user you are. Perhaps you have a narrow definition of resource?
I am thinking of “resource” in the same way I think of “class instance”, basically. That is, a self-contained object of a specific type, with data.
Maybe you are thinking more in terms of “resource provider”? Flickr itself provides many, many resources. But if I access a URI for a specific picture and get a dog in return, anyone else who accesses the same URI should either see a dog or be denied access. It wouldn’t make sense to send a picture of a dog to one person, and a house to another, even though they both requested picture #1938383832.
The view on the resource might be different (bigger/smaller etc.), but the identity of the resource itself ought to be consistent. And I would say that serving different sizes of a picture based on the logged-in user’s preferences is more of a hack to reduce bandwidth. For conceptual clarity the resizing should really take place on the client side (really an MVC separation issue).
Maybe that is clearer? Or maybe not.
Ahh, yes, I was thinking of a resource (rather stubbornly) as being whatever is pointed at by a Univeral Resource Identifier.
I think there’s a distinction that’s made in the REST papers that’s important to this discussion.
The things we hand back and forth on requests and responses are not resources. They are representations. While HTTP isn’t strictly compliant with the ideas of REST in some details, I think it’s more important to be pragmatic than a standards lawyer.
Resources are purely conceptual, and extremely abstract. Simply, a resource is anything we might care to name by a hyperlink. More formally, a resource is a function that maps a uri to a value over time.
It’s entirely expected that the server transform between representation data types and whatever means it uses to store resources on the server. The server’s content of a resource is explicitly opaque in the REST papers, and with good reason.
The ONLY constraint on URI’s is that the semantic mapping be preserved. Notice that this is a judgement that can only be made by the users and developers of a system, not the system itself. It’s a purely semantic constraint.
So, Given that, what’s your justification for
The server itself does not perform transformations on the resource, not ever ever.
beyond rhetoric?
The sever is allowed to do whatever it wants with a resource, provided it doesn’t break the users’s conceptual mapping. All the constraint means is “if I make a hyperlink today, it should point to the same logical thing tomorrow”. Note that this can include explicitly time varying resources, such as “Today’s Weather San Francisco”.
Chris, you’ve got it right here, that’s precisely what a resource is, noting more or less.
CRUD does not span multiple models
sure it can. You just need a model that’s the appropriate projection of the multiple. Conceptually this is similar to an up-datable view in database systems.
no meta-operations in CRUD
Sure we can, we just need to be cruding a command object.
what would CRUD search look like?
A get request? Resources are logical. Our server can expose an infinity of them without actually bothering to go and compute a representation of them until someone asks for it. Nothing says that we have to instantiate or store a record either.
other things inappropriate for CRUD
Crop, contrast and similar can either be calculated server side (bleh), or done via code on demand of an editor capable of generating a preview, then submitting the selected parameters back to the server. Which is precisely what flickr et all are doing.
Follow up is here.
You think DHH is gonna understand all this? maybe it is just easier to tell everyone to EFFOFF and just code like its 19PHP99 all over again.


Coda Hale says (July 05, 2006):
Yeah, that’s pretty much exactly what I was trying to say. Only, you know, well thought-out and elaborated.
Thanks.