APIs

Thus, I've pulled back a bit from my REST absolutism, and I feel like I've reached something of a balance. For creation, updating and deletion, I'm totally fine with using the RESTful paradigm. But when it comes to retrieving resources from a Web application, I've relaxed my requirements, trying to make my API be as expressive and flexible as possible, without creating a huge number of routes or actions in my controller.

For example, I'm totally fine with retrieving information about a single user using the /users URL, tacking on an ID number to get information about a specific one. But, I often want to implement a search system to look for people in the system whose names match a particular pattern. Back in my pre-REST days, I would have used a search controller and had one or more methods that performed a search. In my neo-REST world, I simply add a "search" method to my "users" resource, such that the URL /users/search represents a search. Is that breaking REST? Absolutely. But, I've found that it makes my API more understandable and maintainable for my users, as well as for myself.

Rails itself, as opinionated and RESTful as it might be, offers you an out in the routes file (config/routes.rb). A recent project on which I worked had the following route:


resources :articles

This translates into the usual combination of RESTful routes and HTTP request methods. But when I decided to add three additional API calls to grab particular types of articles, I was able to do this by adding a block to the resources declaration:


resources :articles do
    get :latest, :on => :collection
    get :article_links, :on => :member
    get :stories, :on => :collection
end

Not only does Rails offer this possibility, but it differentiates between "member" routes (which require a resource ID) and "collection" routes (which don't require a resource ID, because they operate on the entire collection).

General Practices

Once you've set up your API-naming convention, you need to consider what data you'll be passing. This means deciding on a data format, the parameters you'll receive in that format and the response you'll send back in that format.

When it comes to formats, there are really two main players nowadays: XML and JSON. As I mentioned previously, XML is very popular among enterprise users, but JSON has become very popular because of how easily you can transform objects from such languages as Python and Ruby into JSON (and back). In addition, JSON is nearly as self-documenting as XML, without the huge textual overhead or the complexity of an XML parser. Like many other people, I've switched to JSON for all of my API needs, and I haven't regretted it at all.

That said, Rails offers the option of responding in any of several different formats with the respond_to block. It basically lets you say, "if users want JSON, do A, but if they want XML, then do B."

As for the request and response parameters, I try to keep it pretty simple. However, there's one parameter you absolutely should include in any API you create, and that's a version number. Your API presumably will evolve and improve over time, adding and removing method names, parameters and parameter types. By passing a version number along with your parameters, you can ensure that you're getting the parameters you require, and that both the client's and server's expectations are in sync.

Moreover, I've found that you can use that version number as a key in a hash of parameter lists. That is, rather than having separate variables in your server-side program that track which parameters are expected for each version, you can have a single hash whose keys are version numbers and whose values are arrays of expected parameters. You even can go one step further than this, having a multilevel hash that tracks different parameters for various API calls. For example, consider the following:


EXPECTED_PARAMS = { 'location' => {
    1 => ['longitude', 'latitude', 'altitude'],
    2 => [longitude', 'latitude', 'altitude', 'speed', 'timestamp'],
  },

  'reading' => {
    1 => ['time', 'area', 'mean', 'good', 'number'],
    2 => ['time', 'area', 'mean', 'good', 'number', 'seen', 'span',
    ↪'stdDev']
  }
}

Then, you can do something like the following:


version_number = params['version_number'].to_i
method_name = params['action']
required_fields = EXPECTED_PARAMS[method_name][version_number]

This is far easier than a lot of if-then statements, and it allows me to centralize the definition of what I expect to receive from my API clients. If I wanted to validate the data further, I could make each array element a hash rather than a string, listing one or more criteria that the data would need to pass.

Finally, the response that you give to your API users is your public face to the world. If you provide useless error messages, such as "Something went wrong", you'll presumably discover that developers are less than happy to use your system or develop on top of it. But if you provide detailed responses when things go well and poorly, not only will developers enjoy your system, but their end users will as well.

Laptop graphic via Shutterstock.com.

______________________

Reuven M. Lerner, Linux Journal Senior Columnist, a longtime Web developer, consultant and trainer, is completing his PhD in learning sciences at Northwestern University.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

At a minimum, I would want

Tony lincle's picture

At a minimum, I would want the box to say "Live Data", Some of the more handy ones will scan the truck, record the data and download it to your computer. Many pull data from the trans, ABS, and other systems.
Truck Adblue Emulator for IVECO

qswatches

qswatches's picture

In the winter, it's quiet, everything seems to be to the original state, as the time to stop down. But it is also and such rise, and seem to be pregnant with new life.rolex daytona & celine bag 2012

Traditional Chinese clothing

Anonymous's picture

Traditional Chinese clothing is usually worn loose and comfortably so as to not get in the way of work and daily activities. An exception to this in ancient times was footwear. It was considered beautiful for women to have exceptionally small feet, especially during the Sung Dynasty of the 1100s, and thus the feet of girls were tied tightly in bandages until the bones were broken and their feet were curled almost in half. Chinese Fashion

django apis example

kiddhustle's picture

Nice article that gave me a refresher on how a restful api should be structured.

Now are there any examples/ codw snippets showing how an api can be constructed in django? Currently looking at getting started with framework but I have no experience as yet.

RE

Johanna's picture

RSS Feeds are indeed not geeky, but they are a bit old, nowadays people connect everything through API's, not through RSS feeds (like prepaid-mobiel.nl ). Welcome to 2012.

absolute REST example?

eMBee's picture

how would a search look like if you follow the REST absolutism you mentioned? i am only starting to look into REST and i can't even conceive why using actions in the url would be a problem. how do you do that then?

what about a resource like /airplanes/523/passengers
or /people/2341/address ?

wouldn't that be the same thing?

as for your routes example, how do the resulting urls look like?

/articles/latest
/articles/123/article_links
/articles/stories

and are those good or bad examples for a REST api?

greetings, eMBee.

Web APIs

Ed Carp's picture

This is what web services are for, and they've been around for a long time. There's nothing new here - output from wherever, wrap XML or JSON or even CSV text, ship it off to a consumer. Consumer strips off the JSON/XML/CSV and does whatever it wants with it. Pretty straightforward, actually.

I'm not sure why the author used a few thousand words to describe what I just did in less than 100. More words isn't necessarily better, unless you're getting paid by the word!

Well, it's not all about data

Ayesh's picture

Well, it's not all about data output. Client can send data to server, server and client can authenticate each other, and there are many actions. Yes it has been there since a long time but it's not all about data output. It's 2012 and rss feeds are no longer geeky.

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix