Developer Blog

A look into the world of Picatic.

Bikesheds vs json:api

Crafting the perfect REST API challenged our team in ways we did not anticipate. Our solution ended up turning our assumptions inside out.

In the planning stages for our v2 API we wanted to address all the cons our v1 API had. We also wanted to add more functionality we thought we could use to enhance the API experience. Often we can come to consensus quickly as a team, but we were going in circles on the best practices to do each feature. One useful bit of functionality would ultimately not play nice with another bit of functionality.

It felt like we were planning the the world’s best REST API. In reality we were choosing colors for a giant bikeshed. We were doing our weekly scrum when it hit us, “Parkinson’s Law of Triviality” had struck our engineering team. We were all so passionate about what the next version of the API should and could be that we got lost in the details that did not really matter all that much.

We passed over a rather verbose API spec a few months earlier, json:api. Never thought much of it at the time. We were hyper focused on the idea of a REST API and json:api did not fit the model we had built in our minds. Frustrated at ourselves for delaying this critical decision for too long, we decided to take a second look.

It is Verbose

Our first reaction to json:api was just how verbose a request and a response appeared. Here is a very minimal snippet:

{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON API paints my bikeshed!"
    },
    "links": {
      "self": "http://example.com/articles/1"
    }
  }]
}

This was briefly a “nope” moment for us, but then, in a moment of clarity, we realized software will do all the hard work. What we give up is the simple hand made JSON query to our API, but we had always planned to release a tool on our developer site to make playing directly with the API possible without having to write JSON anyways. End result is we get setup for smarter requests and responses, and for only a few extra bytes per request.

Compound Responses

We struggled with how to handle including relationships with some of our API requests in the new API. It is common to need data from a few resources to do something useful. e.g. Show our ticket widget. The v1 API handled this with a parameter on the request: ?extend=.... We would then include an underscore prefixed field with the relationship data. It worked, but it always felt clumsy in practice.

JSON:api solves this with compound document support. It allows us to include related information in the response, but in a very predictable and terse way. A perk of this format is de-duplication of included relationships, which was a design goal for v2.

{
  "included": [{
   "type": "people",
   "id": "9",
   "attributes": {
     "first-name": "Dan",
     "last-name": "Gebhardt",
     "twitter": "dgeb"
   },
   "links": {
     "self": "http://example.com/people/9"
   }
 }]
}

Including these extra resources is formalized in a similar way to our v1 API.

Sparse Responses

We wanted to allow people to trim their requests down to only the information they needed. In some cases, the whole event worth of data can be a very overwhelming chunk of data, when all we really needed was some very basic information from everything. Our v1 supported this to a degree, but only on the root data being requested, which meant in some cases the relational data could be a substantial overhead. JSON:api gives us a standard syntax to deal with filtering the root data and relationships:

?fields[articles]=title,body&fields[people]=name

PATCH

JSON:api does what the REST community has mostly avoided, use the HTTP methods as they should be used. Creating a new resource is a POST, but updating a resource is a PATCH. Many REST API’s use PUT and/or POST to do those operations. Our industries aversion to PATCH was mostly a because of an old flaw in <= IE8.

Formal Extensions

All this was great, but it basically gave us a very formal specification for a CRUD resource API. What if we wanted to add alternate ways to interact with our API. Turns out JSON:api has a WIP spec for extensions as well. We plan to add some hypercard-like information to indicate possible actions on resources.

Summary

We spent an afternoon picking apart the above issues/features and many other edge cases. We found that we could create a solution to everything we could think of with JSON:api. There are aspects of JSON:api that do not thrill us, but trade off of having a clear cut path to implement far out weights the cons for us.

bikeshed

Parkinson’s law of triviality

jsonapi

api2go