This is the second jutsu in the series ‘Building a blogging engine from scratch with Grape, Ember.js and the JSON API specification’. In this one, we will study the JSON API specification.
The blogging system we’re building now has a name: Bloggy. Yes, it took me a while to find such an awesome and complex name.
In the previous jutsu, we built an API using Grape and MongoDB. We will be reusing the code from that jutsu so if you don’t have it yet, get the code from GitHub.
Master Ruby Web APIs [advertisement]
Want to learn how to build awesome web APIs with Ruby? I’m currently writing a new book titled Master Ruby Web APIs that will show you exactly how to do that. Take a look ;)
The Bloggy series
You can find the full list of jutsus for this series in this jutsu. It’s also the tutorial before this one so don’t hesitate to read it!
Understanding hypermedia APIs
If you don’t know anything about the semantic web, REST and hypermedia APIs, this article [Coming Soon] will definitely help you grasp those concepts.
An overview of the JSON API Spec
In the next jutsu, we will implement the JSON API specification in Ruby by creating a library. Before we can do that, we need to understand the fundamentals. The best way to do this is to read the full specification here but I will go over some of the basics here to save you some time.
First, here is how a JSON API response for a resource looks like:
{
"links": {
"self": "http://example.com/articles"
},
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
}
},
"links": {
"self": "http://example.com/articles/1"
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}]
}
OMG IT LOOKS LIKE XML, IT’S SO UGLY. - A web API developer
Come on. It might look a bit scary but it’s not much more complicated than the JSON documents your APIs output everyday. You will soon like it!
We are not going to use all of JSON API features in Bloggy so I will only talk about what you need to know for now. I really recommend reading the full specification to get a better understanding of it.
Media Type
The JSON API specification is identified by the media type application/vnd.api+json
. You can find more information there.
Top Level
As you can see above, the top level attributes are:
meta
:a meta object that contains non-standard meta-information.data
: the document’s “primary data”.errors
: an array of error objects.links
: alinks
object related to the primary data.included
: an array of resource objects that are related to the primary data and/or each other (“included resources”).
Some of these attributes are optional, feel free to checkout the specification for the details. In Bloggy, we are going to use meta
, data
, links
and included
.
Meta
In meta
, we will put some information about the Bloggy API like its name or a quick description. This is human-readable and not meant for a computer.
It will look like this:
{
"meta": {
"name": "Bloggy",
"description": "A cute little blogging API.",
}
}
Data
In data, we will put the requested representation of a resource. This resource can either be a list or a single resource.
It will look like this for a list:
"data": [{
"type": "posts",
"id": "39f30kgm08j8j49if0d",
"attributes": {
"title": "A cool article",
"content": "Some content."
},
"links": {
"self": "http://example.com/posts/39f30kgm08j8j49if0d"
}
},
{ ... },
{ ... }
]
And like this for a single resource:
"data": {
"type": "posts",
"id": "39f30kgm08j8j49if0d",
"attributes": {
"title": "A cool article",
"content": "Some content."
},
"links": {
"self": "http://example.com/posts/39f30kgm08j8j49if0d"
}
}
Links
Links are used to give the client the current resource link and the links to the related resources. For example, for a paginated list we could provide the following list of links.
The only mandatory links are self
and related
(for relationships).
"links": {
"self": "http://example.com/articles?page[number]=3&page[size]=1",
"first": "http://example.com/articles?page[number]=1&page[size]=1",
"prev": "http://example.com/articles?page[number]=2&page[size]=1",
"next": "http://example.com/articles?page[number]=4&page[size]=1",
"last": "http://example.com/articles?page[number]=13&page[size]=1"
}
Source: JSON API website
Relationships + Included
Finally, related resources will be included as relationships and listed in the included
list. The included
list is only used for compound documents where the related resources are shipped with the main resource. There are other ways of doing this to avoid loading huge JSON documents. However, for Bloggy, I want to load all the tags/comments right away to keep it simple.
Even if that would be fine for tags to be loaded that way in a real application, I’d recommend not including the comments and side-loading them instead. Take a look at the specification to learn how to do that.
"data": {
"type": "posts",
"id": "39f30kgm08j8j49if0d",
"attributes": {},
"links": { "self": "http://example.com/posts/39f30kgm08j8j49if0d" },
"relationships": {
"tags": {
"links": {},
"data": [{
"type": "tags", "id": "93jf3830023"
}]
}
}
},
"included": [{
"type": "tags",
"id": "93jf3830023",
"attributes": {
"content": "ruby-on-rails"
},
"links": {
"self": "http://example.com/tags/93jf3830023"
}
}]
Wondering why included
is at the top level? It’s simply to avoid loading the same resources multiple times if, for example, the same tag is related to more than one post.
The End
That’s it for our quick overview of the JSON API specification. We know enough to actually write an implementation for it and we will do just that in the next jutsu.