How We Designed Our Public API

Public APIs are crucial for enabling deep integration of your service into other applications and platforms. And once you become embedded into a critical mass of platforms, you’re set. The scary part is designing them to strike a balance between ease of use, flexibility, and options for future expansion.

At both iOSDevCamp 2011 and MongoSF 2011 I gave a presentation that included details about how we designed our REST API. I had looked at a ton of APIs that app developers were accustomed to using such as Foursquare, Facebook, and Twitter and took parts from each that I liked. Here are some of the thought processes we followed to design V1 of the Cocoafish API.

URI Paths

In the beginning we started with Ruby on Rails’ default CRUD methods for each object: create, read, update, and delete. For example by using the proper HTTP method, you can get four actions from the same URI.

Create a user:   POST /users.json
Show a user:      GET /users/<id>.json
Update a user:    PUT /users/<id>.json
Delete a user: DELETE /users/<id>.json

If your API doesn’t require additional actions, this is simple to use and works well. However, as soon as you add a couple of other actions:

Search users:    GET /users.json
Checkin a user: POST /users/<id>/checkin.json

It gets more confusing. Now you have even more actions sharing the same URI scheme. And there’s the oddball method (checkin) that has a verb in the path.

As our API grew we made everything consistent by always including a action verb in the path. The HTTP methods (POST, GET, PUT, DELETE) still determine how the data is being handled, however the verb always makes it clear what is being done.

Create a user:   POST /users/create.json
Show a user:      GET /users/show.json
Update a user:    PUT /users/update.json
Delete a user: DELETE /users/delete.json
Search users:     GET /users/search.json
Checkin a user:  POST /checkins/create.json

Notice that checkin has been moved to its own top-level action. Avoiding nested URIs makes things less confusing and adds flexibility. Also, we ditched the REST convention of placing the identifier of the object being acted upon in the URI. When working on the Cocoafish iOS SDK, Wei realized that it’s much simpler to create generic methods that call our server when the IDs are removed.

Therefore, object IDs are always arguments, and the full URI to checkin a user to a place becomes:

POST  api.cocoafish.com/v1/checkins/create.json?user_id=1&place_id=100

The flexibility afforded by taking all arguments out of the URI can be seen when we want to checkin the user to an event instead:

POST  api.cocoafish.com/v1/checkins/create.json?user_id=1&event_id=100

By not creating complicated URI paths for every combination of object types and actions, we greatly simplify the API.

Versioning

Another important aspect to consider when designing an API is versioning. Someday you will need to make incompatible changes to your API. If you can’t synchronously update your clients when the API changes (Words with Friends really annoys me by forcing app updates), then you’ll need to version. Two common ways of doing this are to place it at the beginning of the URI or include it as a parameter:

 api.cocoafish.com/v1/places/search.json
 api.cocoafish.com/places/search.json?v=1

Either way works fine, but I prefer the former since it can’t be forgotten nor mixed up with other parameters.

- Mike

    • #rest
    • #api
    • #sdk
    • #ruby on rails
  • 6 months ago
  • 14
  • Permalink
  • Share
    Tweet

14 Notes/ Hide

  1. spacer starpause liked this
  2. cocoafish posted this
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.