How do I make an API?

Hi!

I'm a new developer trying to get into Ruby on Rails. My team is
developing an iPhone application that wants to call our Ruby on Rails
web-end to store and retrieve data. I've looked on the web and in this
forum but the only things I could find were dealing with scaffolding,
which a) didn't solve the problem clearly for me and b) we're not
using scaffolding.

So, I was wondering what would be the best way to create such an API,
and how can we do it securely?

Thanks!

- Charles

Unfortunately I don't have a good recipe, but I'm trying to develop
something similar (API for my Rails application) and here are my
unpretentious results.
I have made a standalone ruby script and include Rails configuration
there:
require File.dirname(__FILE__) + '/../config/environment'
Thus I have access to all models and their connections.

I have mod_rails + Ruby Enterprise installed, but this API script
works as CGI and doesn't use Ruby Enterprise gems, so you need to
install the same list of gems for normal Ruby and Ruby Enterprise.
Another way is to add a line in your Apache config:
RailsSpawnMethod conservative
But somebody claims that it's too slow.
Another tip is to check for the same ENV['RAILS_ENV'] value for both
Rails and API script, because Passenger overwrites it.

Currently I'm researching different approaches in optimizing speed of
my application. I'm going to make the API script as a daemon, there
are some great tools in Ruby for that. But I didn't select any and
cannot comment them.

A bit messy, sorry :slight_smile: Maybe this helps.
Nevertheless it will be great to share good experience.

Unfortunately I don't have a good recipe, but I'm trying to develop
something similar (API for my Rails application) and here are my
unpretentious results.
I have made a standalone ruby script and include Rails configuration
there:
require File.dirname(__FILE__) + '/../config/environment'
Thus I have access to all models and their connections.

Why is your api not just some extra controllers and actions in your app?

Fred

Why is your api not just some extra controllers and actions in your app?

Will I be able to daemonize the script if it's a controller in my
application?
I think no. This is why I have chosen this way - I need to make my API
script as a daemon.

Btw, eventmachine gem seems to fit my needs :slight_smile: I'm going to use it.
If you have worked with it, all useful tips are welcome.

Why is your api not just some extra controllers and actions in your
app?

Will I be able to daemonize the script if it's a controller in my
application?
I think no. This is why I have chosen this way - I need to make my API
script as a daemon.

Why would you need to? You've already got mongrel or apache or
whatever sitting in front of it (unless you're not doing this over http)

Why would you need to? You've already got mongrel or apache or
whatever sitting in front of it (unless you're not doing this over http)

To say truly, it's my first API script (and the first daemon script),
and I have poor theoretical knowledge in this area. So I have tried to
make something, but maybe in the wrong way.
I just thought, that if I make a controller in Rails application, it
will initialize many unnecessary actions and it may be very time
consumable. In my script I use only limited number of Models, no
templates, no controllers.
Moreover, for API script I need a non-standard port (because some
clients work badly with 80 and 443 ports), but the whole Rails
application should work as normal.

Can this be done inside a regular Rails application? Will be thankful
for good tips :slight_smile:

This could all be done inside a regular rails app and there's very
little reason to do otherwise (you might want to run your api off a
dedicated cluster of mongrels but right now that would probably be
premature optimisation). You claim to be worry about efficiency, but
it sounds like you've already made it massively inefficient by writing
it as a script that reloads the rails environment for each request.
In terms of tips there aren't really - just do it. You'll have to
fiddle around with your apache configs to get it served on a different
port from the rest of your apps but that's the only fiddly bit.

Fred

If you've set up your app with RESTful resources, you can access them
using ActiveResource. Personally, for public APIs I've shied away
from this method.

Instead, I create an apiV1 (or V2, or V3) controller, and make it
accessible at http://example.com/api/v1/{api_method}.api. This way,
I'm able to easily work on new versions of the API without affecting
the previous versions.

For testing the API during development, I use a little library I wrote
called Bricklayer (available here: http://github.com/heypanda/bricklayer/tree/master
).

The downside to this method is that you don't (to my knowledge) have
HTTP verb access restrictions at the routing level, so you're
responsible for checking the request type at the controller level. I
usually drop in a before filter for api methods that should be POSTS
which just checks if request.post? and handles inappropriate verbs
accordingly.

If you're worried about speed... one way I've solved this is by moving
my API implementations to a separate Merb app that piggybacks my Rails
models. The downside here is that you're effectively managing two
applications, which can be a pain both on the development and
deployment side of things (though I haven't had much issue yet). To
make this work, you have to make sure your load balancer/reverse proxy
is sending requests to the right application. You can either set up
some rewrite rules to proxy based on the request, or deploy your app
to something like api.example.com and set the Merb app up as a
separate virtual host.

As far as "security" goes... depends on the app. For me, it's always
been as simple as having the client send along Basic HTTP
Authentication credentials over SSL. So if, for your iPhone app, you
want a user to be able to view his store inventory or something, you'd
just have him enter his un/pw in the client, and authenticate in The
Usual Way.

If, however, you want to prevent people from accessing the API by any
other method than the iPhone app, I'm guessing you'll want to do some
kind of handshake procedure to ensure this... which is totally out of
my realm. I'm guessing that whatever provisions you take could be
undone with some patience and a decompiler (which is why I'm against
embedding authentication information in the app itself).

M

I guess I kind of started writing my reply with "APIs in general" in
mind, and shifted to "iPhone accessing the API". Ignore my comment
about ActiveResource. :wink: