Thursday, August 28, 2014

How to create a Web API no one wants to use. Part 2: HTTP Verbs

Introduction to this blog series


Later this year, Netflix will be closing their public APIs. Twitter and Google have already restricted their APIs. Despite prominent tech-companies making drastic changes to their APIs, the number of APIs keeps growing steadily. Suddenly, everyone has a Web API. Your car has one, Chuck Norris has one, and worst of all, tech-companies with no focus on quality whatsoever has one. And who are using these APIs? Most likely, no one.
This blog series will take you through the pitfalls of creating a Web API. From a developers perspective, what mistakes will result in no one wanting to use your API?

Part 1: URI Design
Part 2: HTTP Verbs
Part 3: HTTP Status Codes
Part 4: Result formatting
Part 5: Versioning
Exercises

HTTP Verbs

There's an old story called "The Spider of Doom" where a company developing a content management system suddenly found that their client had "magically" lost all the pages of their website. After a lot of searching around, they found that the Googlebot was able to bypass their authentication mechanisms and had therefore crawled all their "Delete page" links. This, of course, resulted in all the pages of the website being deleted.

Imagine for a minute that you're creating a Web API that doesn't require authentication, but where the users should be able to delete content. You also want the endpoints to be crawled by search bots. How do you ensure that the bot doesn't delete all your content? Well, you make sure that any deletion of resources isn't done with a HTTP GET request. In other words, you use HTTP verbs in the way they were intended.

HTTP verbs makes it easy for other developers to consume your Web APIs without you having to manage and maintain several endpoints for each possible action. Sadly though, a lot of APIs only use the GET verb, and they use it wrong. On the bright side, that's a pretty great way of making sure no one will use your API!
Example: The Tesla Model S JSON API
I like to call the Testa Model S API the Batmobile API, and it pains me that I'm able to find something wrong with it. Tesla is only the greatest car ever built, and creating an API for it is genious, it only makes me want it more.
Let's start out by getting the current climate state of the car: 
GET /vehicles/{id}/command/climate_state
So far, so good. Remember, GET, PUT and DELETE are idempotent, meaning we can issue a request against them as many times as we want without changing the result. In addition to this, the GET method is safe, meaning it should not have any side effects or change state in any way. If we apply these two rules to the climate_state endpoint, we see that we can issue the request twenty times and we will always receive the climate state of the car in return, without changing the state of the car. So it clearly satisfies the criteria of the GET method.

Now, let's open the sun roof of the car:
GET /vehicles/{id}/command/sun_roof_control?state=open

Here we have a problem. When we open the sun roof of the car, we're changing the state of the car. Therefore, the sun_roof_control endpoint does not satisfy the criteria of the GET method.

Problem: The Tesla Model S JSON API

The main issue with this API is that it calls itself RESTful, but it uses the GET verb for all its endpoints except one. Had the API been implemented according to the HTTP protocol, then the following endpoint should have given us the current state of the sun roof, for example "open" or "closed". It should not change the state of the car in any way.
GET /vehicles/{id}/command/sun_roof_control

Solution: The Tesla Model S JSON API

In order to change the state of the sun roof, we should issue a PUT instead of a GET:
PUT /vehicles/{id}/command/sun_roof_control?state=open

The PUT method is not safe, meaning the request is allowed to change the state of the car. It is, however, idempotent. This means that the request above will always result in the sun roof being opened, whether we issue it two times or twenty. By using PUT, we're now satisfying the critera of the HTTP protocol.

What about POST and DELETE? Well, we can't delete sun roof, so the DELETE verb will not be supported. POST is usually used for creating items, and unfortunately creating a new sun roof is not allowed either.

GET (read)POST (create)PUT (update)DELETE (delete)
Returns the state of the sun roof (open/closed)Not supported, cannot create new sun roofChanges state of sun roof (open/close)Not supported, cannot delete sun roof

There is an extremely entertaining thread on Reddit about the Tesla Model S API and how honk_horn should have been implemented. I highly recommend taking a look at it!

Summary


If you're using the GET verb for all your endpoints even though state is changed, you will make developers using your API wrinkle their noses. Use the correct verbs for the job and you will see that maintaining your API will also be a lot easier. Also, do yourself a favour, and care about your API from the very first piece of code you write. 


1 comment:

  1. Thanks for this. Looking forward to next article in the series!

    If someone wants to dive in here's another good discussion: http://stackoverflow.com/questions/630453/put-vs-post-in-rest

    ReplyDelete