On RESTful API Standards – Just Be Cool: 11 Rules for Practical API Development (part 2 of 2)

2 Comments

I posted a few days ago about my ideas for 11 rules for practical API development. Let’s jump back in with rules 6 to 11.

6. Make it easy to understand and navigate through child objects. The fact is it can be really easy to confuse your database fields as API fields. You might have a field in your table called color_id that represents a color in the color table. Your service returns color_id and you expect your users to somehow know to hit the color service to find out more information about the color. It will behoove you to represent the color as an object since it is an attached object. Also, including a URL where the user can get more information about the color is super helpful. And if you have an extra minute, throw in the URL where they can see all the colors in the meta part of your response. For example: {…”color” : {“id” 3, “name” : “blue”}…}{“meta” : {“urls” :  [{“color” : “http://yourcoolapi.com/color/”}]}}. Use a convention like that and developers will be able to add code to automatically pull in more information about the object as well as pull all objects for a picker list.

7. Allow users to limit which fields they receive. If a user wants to only grab the id and name of 300 objects, why make them retrieve all of the fields on each object? It is a lot of data for them, a lot for you, and it just makes everything slow. Maybe this breaks the rules, but it will save everyone so much time and resources.

8. Allow users to modify and access child objects directly. (Confession: As a developer, I rarely implement this.) If you have a hierarchy like menu->courses->dishes, your users should be able to add/update/remove dishes from a specific course without sending the entire course or menu object. Usually this is done by allowing people to POST to a URL like menu/7/course/1/dish to add a dish to the menu with id 7 and course with id 1. Updating a dish can be done via put and removing via DELETE to a URL like menu/7/course/1/dish/13.

9. Enable versioning. I don’t care if it is in the URL or the header – just allow people to hit a specific version. This way, you can make improvements without breaking old APIs. I’m very fond of the format: yourfavoriteapi.com/v1.1/service. I know some people say putting the version in the URL breaks REST and kills kittens, but oh well. It’s so easy to implement and to consume. Most users who implement your API will have the base URL as some sort of static of config variable, so including the version in the base URL makes it super easy. Foursquare does this cool thing where you pass in the date in which you are working on your code. They can then cross reference that against release dates and figure out which API version you are using. It is a pretty cute solution for users having to increment versions since version numbers don’t make sense to users and users will typically upgrade to whichever the most recent version is. Though for me, I think I will stick with version numbers.

10. Feel free to break the rules of the URL every now and then. Some people are completely against passing GET parameters to your services. They say you should somehow pass pagination requests through the headers. I personally feel that if you are doing some sort of filtering on a data set, feel free to pass those parameters in the GET –  pagination, sorting, etc. And while you are at it, feel free to throw in the format of the data (json, xml, etc.). I know people are saying “HAI you cannot put .xml at the end of the service request! That makes it look like a different object! Put it in the content types that you accept!” That’s cute and all, but you can have multiple representations of the same item in different file formats. Imagine downloading a logo that comes in PNG and GIF form. Is it that crazy for identical images to have the same file name but different extensions? No, it is fine. Don’t over-think things.

11. Include pagination links on GET. As your API grows, you will want to paginate results. Come up with a scheme of how that works, but don’t expect the user to all of a sudden know when a service needs pagination or how to build the URL for the next set of data. Include URLs for the next and previous set of data. Now users can write generic wrappers and if they see that next, they can keep iterating through until they have all of their data. Easy peasy.

This entry was posted in Developer Tools, Development Process. Bookmark the permalink.

2 Comments
  • http://pulse.yahoo.com/_EJO72JN2UKUFKNPAMOAFZKVAKY Bryan

    6. HATEOAS, yes! But I think you are missing out by just having bare URIs. Links should be decorated with their own attributes, especially a “rel” (for relation) attribute, because clients can find links with less coupling to your specific data format. Look at how ATOM does pagination with “next”, “prev”, “first”, “last” link relations.

    7. Yuck. This is infected with RPC thinking. You do not want clients to have to construct URIs. It’s MUCH faster to cache full payloads if you do it properly. If I want fields 1, 2, 3 and the next requester wants fields 1, 3, 5 and we get a cache hit the avg response time is better. The slow part is when the database goes to disk to get the data block with the row in it. It’s getting all the fields anyway. Your way assures we do this twice. Caching is your friend. Use it.

    8. Refinements are good. Do this INSTEAD of #7.

    9. Versioned URIs are a rookie mistake. This and point #7 are items where it’s just SO MUCH easier if you don’t do it this way. Everybody loves versioned URIs until /v2 and then their clients come and choke them and they realize the value of permalinks. The **API** version does not belong in the **RESOURCE** identifier. This forbids reuse of resources across versions of the API, which is exactly the opposite of what you want. If you want to juggle all the resource locations, use a new hostname v1-1.yourfavoriteapi.com and go pour milk on your head, because you are being a jerk to your clients.

    10. Um, there’s no rule against GET parameters. Really. REST doesn’t impose ANY restrictions on the format of your URIs. The point is your clients should not use those parameters like they are method variables. The URI should be an opaque string to them. Clients should NEVER construct such calls. If you hand them URIs that have parameters, fine. If you are expecting them to put the customer name in http://example/customer?name={customerName} you aren’t getting what REST is all about. HTTP allows this, REST does not.

    REST is an architectural style. If you are telling your clients to build API calls to you by putting the customerName in a URI like that and calling it REST, it’s like building round ice houses and calling it Roman architecture. You are just bastardizing the terminology. It doesn’t mean that round ice houses are useful in some situations. But it is flat wrong to call it Roman architecture. It’s also probably going to bite you in practice, because, um, they’ll melt.

    As far parameters for XML vs JSON, there is nothing wrong with this if you are targeting people viewing them in browsers. It’s nice to use the browser to navigate to some page that lists whatever and then stick .xml or ?format=json on the end for raw data. But content negotiation is a much better way if you aren’t targeting browsers.

    11. Lack of pagination is a denial of service vulnerability for both client and server. It’s exactly the same as a buffer overflow. Paginate *every* collection. We never expect clients to construct the pagination links, because we never expect them to construct ANY links. Use links with the standard IANA link relations of “next” “previous” “first” “last”.

    Looking over your comments, two themes stand out at me. HATEOAS and media types. The value of HATEOAS is that it forces a whitelist of things that clients must understand to use our API: HTTP, the media types we use, and link relations we use. That means we can make any changes we want and be safe as long as we preserve these things. When you start making rules for clients to be aware of like how you use URI parameters or that you always put your object name as the root of your JSON, you create a body of special “out of band” design knowledge that complicates everything.

    • disasterBalls

      On 6. Yes, on the attributes thing – JSON-LD anyone? Or just stick to HTML/other XML-based linked data?

      7. & 8. – I’d lean towards (the plural bit is up for debate)
      GET course/1/dishes – to provide a list of links to dishes in course 1.
      Likewise, GET course/1/dish/4/attributes – to provide a list of available attributes for course 1 dish 4 so we can ask for them as independent resources, e.g. GET course/1/dish/4/alergyadvice – to return allergens it contains/doesn’t (possibly with onward links)

      That said, I don’t really have a problem with query string being used to filter the list of returned attributes – it does mean having a different caching scheme than just using output cache, but sometimes I end up with that anyway to give greater control over cache expiry.

      However, conceptually, rather than query string parameters, it should probably be a POST to resource/view with the values for the filter, (in theory this may require an asyc process to retrieve), then a GET of resource/view/ to return the restricted view of that resource…?

      On 11. I think its important we include a seperate page list resource alongside the next/back/first/last that would come with each page.
      e.g. resource/pages. Just for the practicality of use with UI building with a page selector – the client doesn’t work out the paging, we provide a list.

      p.s. as my day job is in .Net, I’m yet to find simple told to build to the level described here, so if anyone has any pointers to avoid some of the manual mapping of this stuff it would be much appreciated! :)