We’ve been pretty dogmatically RESTful since our API was first written–not that it was too difficult considering Rails’ built-in proclivity to it. However, I recently confronted a issue with REST that has split some devs out there, and I’d love an opinion:
What is the best resource URI for multiple but discrete resources?
For instance, I am working with our Render objects (incidentally, for the Rails people out there, that naming has surprising _not_ come back to haunt us), and the RESTful endpoints for this resource are as follows:
/renders [GET, the collection method "index"]
/renders [POST, the collection method "create"]
/renders/1 [GET, the member method "show"]
/renders/1 [PUT, the member method "update"]
/renders/1 [DELETE, the member method "destroy"]
Any developer familiar with REST paradigms will immediately recognize those. However, what if I want to load more than one render object in a single query, but I want to use individual IDs and instead of getting a complete index listing?
Two ideas sprung (sprang?) to mind:
Apparently, Highrise uses the latter protocol (or at least, this post from 2007 says so), but then I got to thinking, and suddenly that didn’t seem right. Here’s why:
In my mind, in spite of my general aversion to query params (and the fact that using IDs in query params is inconsistent with the more typical case of IDs being used in the URL routing), the one that uses query params is actually more correct because the root endpoint, /renders, returns what is expected, what it normally does: a list of renders within a “renders” block (in XML, which we use). Moreover, it’s pretty common for index/listing endpoints to accept query parameters to filter (or paginate) the results, so an “ids” parameter (which, incidentally, I prefer to “id” for its conspicuousness) can just be seen as yet another filtering param.
If you go with the latter protocol, it’s true that you can get Rails to parse the “1,2″ into params[:id], which seems clever and convenient (and plus you get to learn about ActionController::Routing::SEPARATORS), but it then becomes an endpoint (a controller and action) that returns inconsistent data formats: sometimes it returns multiple renders in a “renders” block, and sometimes it returns a single render with no “renders” block.
The one advantage I can see to the latter method is access control: we have some consumers that are allowed to use the /renders endpoint for index/listing of renders, and others that are not–and I would like all consumers to be able to list multiple renders with multiple IDs, not least because it probably means they’re cutting down on further API requests. With the latter protocol, this is handily handled for us; with the former, it’s not. However, this is pure coincidence and, like the auto-parsing into params[:id], shouldn’t be taken as a guiding point of evidence: an easy work around is just to pass the request off from the “index” to “show” action after all the access control before-filtering has taken place.
My suspicion as to why this isn’t more clear out there is that REST was never meant to load multiple objects: you should just make multiple requests to the singular resources. However, in the real world we have real reasons to want to do this, and I don’t think it behooves us to slalom down the mountain of Roy dogma.