Successful REST API Design

Posted by Fernando Doglio | Jan 21, '14

Screen_Shot_2014-03-31_at_7.43.28_PM

Lately the REST "word" is being thrown into the digital wind very often, and often enough, it's not being fully understood, so as the concept gets twisted into incomplete versions of it, the products that use it end up missing some of the good parts.

In this post, we'll try to give a complete look of what REST is meant to mean and some of the standards being used nowadays on the software industry.

So, lets start with a small definition of how was REST defined when the concept was born:

REpresentational State Transfer

Roy Fielding presented the idea of REST on his doctoral dissertation, in it, he described the principles behind the concept. We'll be basing our difinition out of that paper.

From Fielding's dissertation:

The Representational State Transfer (REST) style is an abstraction of the architectural elements within a distributed hypermedia system.

So, to put it in plain english, REST is a architectural style that can be used to organize distributed systems. It is a style and not a standard, there is no RFC out there defining REST, and as such, it has and (to a lesser degree) it still is suffering from some miss interpretations that cause weird and semi implementations of this.

Lets go into some more details.


What's REST good for? What can we accomplish with it?

REST was designed with the following objectives in mind:

  • Performance
  • Scalibility of components interaction
  • Simplicity of interface
  • Modifiability of components
  • Portability
  • Reliability
 

With this, we can extrapolate some of the advantages we would be adding to our architecture if we were to follow the REST style:

  • Component designed architecture allows us to make our systems very fault tolerant.
  • Connecting components is very easy, making it super scalable when adding new features.
  • Platform and language agnostic. We can use our product from any platform using any language capable of an HTTP connection.
 

Constraints

In order to assure the above benefits, the REST style sets some constraints to the architecture.

Client-Server

By separating concerns between client and server, we make sure that the client is not required to know anything about things like the storage layer, or that the server does not have to care about things like the UI implementation. Thus making our API more scalable and portable. We're also making it possible to rewrite and replace each component independantly while maintaining the same interface.

Stateless

We'll be constraining the client-server connexion even further by having it be stateless. This means that any client-side state information has to be maintained there, on the client side (or could possibly be sent to the server-side to be stored on an external storage, like a database).

Cacheable

One of the most ignored constraints out there. All of our API responses have to be defined (in any way possible) as cacheable.

Code on demand

Fielding explains this constraint in his dissertation, as the ability of the servers to expand their functionality by simply downloading and executing code, like a JAVA Applet, or a Ruby file. This is the only optional constraint of the list, due to the fact that it presents both an advantage and a disadvantage (it reduces code visibility) so it has to be used only when required.

Layered system

By adding the concept of layers, we're splitting our architecture into components. Hierarchically organized components, which in turn assures us that our components will only interact with each other is needed.

Uniform interface

One of the key aspects of REST, is the Uniform Interface constraint. This allows our architecture to provide an implementation independant point of access. Also, by keeping a uniform interface, we're standarizing the way the clients will interact with our API. Even though this may be seen as a point in favor of scalability, it's also a point against performance since providing a standard access format and a standard method of delivery maybe not be taking into account specific client conditions that may improve performance.

 

Thinking about resources

So, lets cut to the chase, enough with the tech-talk. One of the main aspects of REST APIs, and a very important concept when designing one, is thinking about Resources.

We'll be constantly dealing with resources, be it web pages, images, search results, etc. As long as we keep this concept in mind, our API's design, will be focused on resources, instead of actions (like the old SOAP used to be), and our endpoints will be very intuitive.

Resources can be static or dynamic, that doesn't really matter, what matters is that we keep their identifiers static, and since we're using HTTP, the way to identify a particular resource is by using a URL.

Remember the three R's: Resources, Resources, Resources. If we do it this way, our endpoints should look like this:

  • GET /photos //Get the list of photos
  • GET /photos/123 //Get the photo with id 123
  • GET /photos/123/people //Get the list of people tagged in photo with id 123

Resources-focused endpoints are a great way to make our interface uniformed.

But what if instead of getting an image, we want to upload a new one? We can't go back to the action-based endponits again, like /upload_image. So enter the HTTP Verbs.

Verbs all the way

We need a way to represent actions, and we want to keep our endpoints clean. The solution to our problems is already there, actually, you're already using it, maybe even without realisinig it.

HTTP Verbs: we'll be using them to define the actions we want to acomplish.

We all know about GET and POST, but there are other, less known, verbs that will come in handy, more specifically: DELETE and PUT.

Remember: REST is not a standard for API design, which means that, again, there is room for free interpretation of Fielding's work.I say this, because there is no standard for the actions that these 4 verbs are meant to represent, but there are styles.

A very commonly accepted distribution of responsabilities amongst the verbs, is the following one:

  • GET: Get a resource
  • POST: Create a new resource
  • PUT: Update an existing resource
  • DELETE: Delete an existing resource

There might be some discrepancies with POST and PUT, but GET and DELETE are pretty much self-defined, don't you think?

Fantastic! We now have a way to access our resources, and we know how to define the basic 4 CRUD functions, but what if we need some extra room for wiggle? What if some of these actions actually have some extra functionality, what if we want to sort the list of photos by name?

Here's a good principle I picked up over the years of RESTing:

Hide the complexity under the "?"

Keep the endpoints easy and intuitives, and any added complexity it may have, just put it as a query parameter. So, to continue with the example from above, the list of photo's endpoint, should be something like this:

GET /photos?sort=[asc|desc]

Versioning

Another very important aspect of our design should be versioning of our API. It is crucial that the developer using our API is aware at all times of which version of it is being used, and we should provide a way for them to always target a specific version, that way we have room to grow and change our API in non-backwards compatible ways without affecting anyone.

There are several ways to accomplish this, we could provide a versionless endpoint which would point to the latest version (which could be dangerous if we do some drastic changes), we could hide the version inside a query parameter, defaulting to the latest version if not present or we could just add the version to very endpoint's URL and make it mandatory, that way everyone's always aware of which version is being used.

There area different versioning schemes that can be used when picking the API's version number, but a good and very accepted one, is based on the significance of the change. So we would have numbers on our version:

major.minor.patch (i.e: 1.2.0)
  1. Major changes are the ones that leave our new code being non-backward compatible. Changes in this number are the ones that affect the most to our consumers, so they have to look out for that.
  2. Minor changes, are things like new features, big bug fixes, and things like that. They won't affect current consumers directly, but they will probably provide a better experience.
  3. Patch changes are the smallest ones, fixing typos in error messages, fixing minor bugs, etc. They are surely enough, backward compatible and our consumers will most likely not really care about them.

So, having that small definition in mind, we could potentially leave out the last number (patch) outside of our URL.  Now our URL should start looking like this:

VERB /VERSION/RESOURCE
 i.e:

 

  • GET /v1.0/photos
  • DELETE /v1.1/photos/1234

(The format we use for the version portion is irrelevant, as long as we use the same for all of our versions, no one will really mind).

There is JSON on my response!

Yes, there is, and this is not strictly specified in Fielding's paper, but the current trend on the API design "world", is that JSON rocks. Lets face it, it is a pretty good way to deliver structured information, specially compared to the one used before: XML.

JSON provides several advantages over XML that have won it a place amongst most of the modern APIs:

  1. It's light weight when compared to the overly verbosed XML format. This means less transfer time between client and server.
  2. It's easier to read by humans, specially developers who're constantly dealing with it during the development stage.
  3. JSON has support for different data types, whereas XML on supports text.
  4. And we could go on...

That's not to say JSON is better than XML in every way, and that's why sometimes it's better to provide to option for clients to specify what response format they support and for our API to provide both types. Twitter's API does exactly this, they will provide a JSON format and an XML format for every response, the client has to specify the one it'll understand.

Hypermedia on the response (A.K.A Self discovery)

There's still one huge issue that we haven't covered: Self discovery

Think about your favorite web site, all you need to know about it in order to browse it, is it's home page url, that's it, after you're there, the links will allow you to navigate wherever it is that you want to go inside it (a contact form, a list of news, your profile, etc, etc).

Our APIs should work the same way, we should provide a single static point of access, and afterwards, we sould be in charge of telling our clients, where to go for each possible action. How do we accomplish this? By using Hypermdia links, just like that website we just talked about. Each resource and each action must have a unique identifier, which as we previously mentioned, is going to be a URL, so every response we send back to the client, will have 2 things:

  1. The resource or resources requested.
  2. Metadata that will enhance the response by adding things like:
    1. URLs of follow up actions (you requested a picture, would you like to see who's tagged in it?)
    2. Data type of those links
    3. Extra information that's relevant, like current version of API being used, last modification date of the resource, etc.

Let's show an example, using the following endpoint:

GET /photos/123


{
 "resource": {
 "url": "http://s3.amazon.com/my-bucket/smaple.jpg",
 "type": "image/jpeg",
 "id": 123
 },
 "metadata": {
 "links": {
 "people_tagged": {
 "url": "http://myhost.com/photos/123/people",
 "type": "text/json"
 },
 "list": {
 "url": "http://myhost.com/photos",
 "type": "text/json"
  }
  }
 }
}

The name of the keys may change, and the way the information is arrange could be different as well, but the sentiment remains: you get the resource you're looking for and you also get some extra information telling you where to go from there; in our case your could go back to the list of photos, or get the people tagged in that one.

This approach gives us, as API creators, the flexibility of changing the internal urls in any way we want, without affecting the clients, which is a pretty cool thing to have.

Again, there is no set standard for the format stated above, but there are some styles that could be useful to follow, for instance, one that plays quite nice with JSON and XML is called: Hypertext Application Language or HAL, you can read more about it here: http://stateless.co/hal_specification.html

HAL provides a nice set of conventions that the API and clients can follow to ease the integration process.

To sum things up

When designing a REST API try to:

  • Take into account all of the constraints stated by REST, allowing you to take advantage of all of its benefits.
  • Remember to three R's: Resources, Resources, Resources.
  • Take versioning seriously and don't let the client guessing which version are they using.
  • Let the client pick the response format.
  • Remember: self discovery = more flexibility for you = happy developer.

Topics: rest, api, xml, hal, json, Tech Tutorials, Web Application Development, Development News

Have an Idea?

Posts by Topic

see all

Subscribe to Email Updates

Our Slidedeck

Screen_Shot_2014-03-21_at_10.09.02_PM

Need a job?

View Job Board