Back to Tutorials
Jutsu6/8/2025

Bloggy #2 -Bloggy: Understanding the JSON API specification

apigrapehypermediajson apirestruby
Bloggy #2 -Bloggy: Understanding the JSON API specification

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: a links 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.

Comments

Loading comments...

Level Up Your Dev Skills & Income 💰💻

Learn how to sharpen your programming skills, monetize your expertise, and build a future-proof career — through freelancing, SaaS, digital products, or high-paying jobs.

Join 3,000+ developers learning how to earn more, improve their skills, and future-proof their careers.

Bloggy #2 -Bloggy: Understanding the JSON API specification | Devmystify