After struggling with it probably a little bit more than I should have, I’ve totally come around to the REST way of looking at things. It seems clean and nice, and I’m 100% behind it. That said, I’ve found that once you adopt the mentality, things that don’t gel with it start to stick out like the guy in the fur coat at the PETA meeting.
Case in point: the ever-popular Rails auto_complete_field helper. To back up my accusation here, let’s walk through an example scenario with a REST app, and examine the issues that arise…
Imagine a really simple web application which consists of a collection of Articles. The only user interaction is that guests can comment on articles and tag them with keywords. So in a RESTful world, we have an Articles resource, and a Tags resource. Tags are also nested on Articles such that when you ask for /articles/:article_id/tags you’re asking for the subset of tags which have been applied to article_id. We might present these as a tab you can click on when viewing an article resource in a browser.
Now let’s say that there’s a form somewhere (maybe the #new action on an ArticleCommentsController), which can be used to apply new tags to a given article. There’s an auto_complete_field in that form, that will suggest tags to apply to an article, based on the tags that have already been applied. Dumb example, I know, but hear me out.
So, given this scenario, it would make sense to point our auto_complete_field helper at article_tags_path(article_id). This is about as RESTful as it gets — we’ve become a client of our own API. Nice! Our #index action on ArticleTagsController, in a perfect world, would do a couple things above and beyond just displaying the pretty HTML list of tags on the article:
- It would have a respond_to block. if a browser client is asking for XHTML, we probably want to render a nice paginated view of the relevant tags, ordered by weight, with a nice layout. On the other hand, if we’re asking for XML or JSON, it just returns no-frills data in the appropriate format.
- it would optionally check for a filter query string. perhaps we set params[:filter] = :attribute_name and then make the actual string filter value (the thing we’re trying to help auto-complete) available via params[:attribute_name]. in the case of our tags example, we’d end up with a params hash that has filter=name and name=zer (or some other text string). If a filter parameter is found, we’d filter the results by the pattern specified. This could all be wrapped up in a nice little helper.
More importantly, if the auto-complete filter is being applied to the collection of resources, that means it maps to the #index action by way of a GET request. So our parameters end up encoded in the URL with a query string like ?filter=name&name=zer. Obviously, GET params are a little more limiting than POST params in terms of length and (perceived) security. But for simple filter strings that take a short sequence of characters, I don’t see this as being an issue. And this is always the case when doing an auto-complete, as far as I can figure.
A perfectly legitimate half-way solution is to define a standard #autocomplete action for any resource you want to use in this manner. The advantage to this is that you can easily use a POST against a custom :collection method on the resource. That way, we continue to work inside the existing box of conventions. But I ask you, is that really RESTful?! What do you think?