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
- starpause liked this
- cocoafish posted this