May

REST on Rails - filling in the missing pieces
at play/type, we’re building a social event calender called boomloop.com. the boomloop API is a REST based API. It can be used to publish and fetch events on boomlop. you can find the api description here: http://code.google.com/p/boomloopapi/
building the API showed some shortcomings in rails out of the box REST API. Here’s a vanilla implementation of a show method:
A standard Rails REST controller
def show
@post = Post.find(params[:id]) respond_to do |format|
format.html
format.xml { render :xml => @post }
format.js
end
end
calling the xml representation looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<posts type="array">
<post>
<id>1</id>
<title>moo</title>
<body>grazing. lovely. </body>
<author_notes>note to self: really must improve my crappy posts.</author_notes>
<author_id>43</author_id>
</post>
</posts>
Where’s the problem?
- we’re returning internal data – author_notes is private data. attribtutes should be whitelisted by default.
- there’s a bad smell on our keys and foreign keys. id and auhtor_id expose our id internals, but worse, they don’t talk about resources using urls. the external representation of a restful resource should be a url.
Further:
- authentication with http basic sucks. we have much better options these days – the api should support oauth.
- we want this stuff out of the box: sensible defaults.
Getting events closest to your current location on boomloop.com
I’ll show you how we solved these problems at boomloop.com, using an example.
Say you want to get a listing of events closest to you current location, opernplatz in frankfurt am main.
curl 'http://api.boomloop.com/events?order=proximity&origin=opernplatz,frankfurt+am+main'Oops. This will tell you you’re not authorized. Grab your oauth client token key by checking your application under http://boomloop.com/oauth/applications. You need to put this into your header like this:
curl -H 'Authorization: loopy consumer_key="klHgtzJ78jko7Jh7g"' 'http://api.boomloop.com/events?order=proximity&origin=opernplatz,frankfurt+am+main'Authentication
API authentication is handled by the rails OAuth plugin. You need to register your application before you can go. We decided that you can pass your oauth client key in a special header to identify you for this request. Protexted resources (such as create event) require the client to have negotiated the permission using the normal signing process.
I don’t know if this is the best way of doing things, since this way, the consoumer key can be passed around. Also, the header is a bit invented.
If you want to try this out, go to boomloop.com/api to register an application, the replace the consumer key with the key you received.
XML Structure
Now you should get some results that look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<events type="array">
<event>
<all-day type="boolean">true</all-day>
<attentions-count type="integer">1</attentions-count>
<cached-tags>opernplatz fest frankfurt</cached-tags>
<category-id type="integer" nil="true"></category-id>
<created-at type="datetime">2008-05-01T20:16:18Z</created-at>
<description>Diesem Leitsatz folgt auch das Opernplatzfest, das auf gehobene Weise kulinarische und kulturelle Genüsse auf das Schönste vereint. Ursprünglich als Brunnenfest rund um den Lucae-Springbrunnen entstanden, hat sich das Opernplatzfest im Laufe der Zeit zu einem zahlreich besuchten Sommerfest entwickelt, dessen einmaliges Flair nicht nur Banker und Werber anzieht.Getreu dem Motto "Sehen und gesehen werden" trifft man sich hier gerne unter den großen weißen Sonnenschirmen, um bei einem Gläschen Wein oder Sekt internationale Spezialitäten zu genießen. Die Gastronomiestände bieten den Besuchern ein großes Spektrum an Spezialitäten, von spanischen Tapas über frische Meeresfrüchte bis hin zu japanischen Sushi. Für musikalische Unterhaltung sorgen bekannte internationale Künstler, die auf mehreren Bühnen Live-Musik spielen. Besonders reizvoll wird die Stimmung des Opernplatzfestes am Abend, wenn die Fassade der Alten Oper und der Platz um den Brunnen mit vielen Lichtern feierlich beleuchtet werden.</description>
<end-time type="datetime">2008-06-13T23:59:00Z</end-time>
<ends type="boolean">true</ends>
<geo type="geometry">50.115848, 8.672066</geo>
<language>de</language>
<main-photo-resource-url type="link" mime-type="text/xml" rel="Photo" nil="true"></main-photo-resource-url>
<mapped type="boolean">false</mapped>
<picture nil="true"></picture>
<place-resource-url type="link" mime-type="text/xml" rel="Place">http://api.boomloop.com/places/389-opernplatz</place-resource-url>
<start-time type="datetime">2008-06-13T00:00:00Z</start-time>
<title>Opernplatzfest</title>
<user-resource-url type="link" mime-type="text/xml" rel="User">http://api.boomloop.com/users/dribbdebach</user-resource-url>
<website nil="true"></website>
<resource-url>http://api.boomloop.com/events/537-opernplatzfest</resource-url>
</event>
</events>
Notice that all ids have been replaced by a new node type, “link”. links have the general form of
<user-resource-url type="link" mime-type="text/xml" rel="User">http://api.boomloop.com/users/dribbdebach</user-resource-url>
this tells us that the link points to a User, in this case the user that created the event. staying restful, we have the full url to the text/xml representation of that user: http://api.boomloop.com/users/dribbdebach
For assoications, this strikes a compromise between something like ATOM, and the RESTful XML used by rails as a basis for Active Resource.
Writing to the API
What goes out, should go in: assocations are handled the same way when saving over the api. so user-resource-url also works when saving an event.
Code
We wrote a plugin to handle whitelisting (xml and json) and association de-referencing automatically. This is a first step in the direction of sensible defaults. If anybody is interested, i can publish the plugin and explain the code in detail in a second post.



Comments
There are 0 Comments for this post. Write comment →
Write a comment
Required in bold.