play/type blog

We are creating Germany's juiciest event platform, boomloop.com. Because we love the Internet more than our own mothers. See for yourself. check out boomloop.com


20
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?

  1. we’re returning internal data – author_notes is private data. attribtutes should be whitelisted by default.
  2. 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:

  1. authentication with http basic sucks. we have much better options these days – the api should support oauth.
  2. 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&#252;sse auf das Sch&#246;nste vereint. Urspr&#252;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&#223;en wei&#223;en Sonnenschirmen, um bei einem Gl&#228;schen Wein oder Sekt internationale Spezialit&#228;ten zu genie&#223;en. Die Gastronomiest&#228;nde bieten den Besuchern ein gro&#223;es Spektrum an Spezialit&#228;ten, von spanischen Tapas &#252;ber frische Meeresfr&#252;chte bis hin zu japanischen Sushi. F&#252;r musikalische Unterhaltung sorgen bekannte internationale K&#252;nstler, die auf mehreren B&#252;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 -resource-url, as well as a rel=type telling you what this link is about. so

<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.