JSON:API consumption in Rails

Jasiek Matusz
Visualitypl
Published in
4 min readMay 6, 2019

In the world of modern web applications you cannot avoid becoming a heavy user of APIs. Consumption of different interfaces brings the experience, and experience brings knowledge.

One of the many things we learned at Visuality is that its not only okay to try new things, but it is also even better if other people tried these and have some insights on how to actually do it the good way. Over time we researched and developed ways to introduce JSON:API into our Rails applications, so that we could be compliant with this specification, thanks to which the development process would be more effective and time efficient.

If you would like to learn more about JSON:API consider checking out Nadia’s article about JSON:API and NIH syndrome.

Let’s get down to business now, and as a backend engineer at Visuality, I will be your guide today!

Shortcoming

There are many implementations of this standard (you can find a comprehensive list here), but what we recognised is that most of them usually work one-way: to serialise the data we have in the Rails application so it can be served to the API consumer. The major shortcoming was that there was no go-to solution for receiving and understanding the input the consumer sends to our application.

The ones that supposedly were to support input deserialisation either did not work 100%, were abandonware or weren’t supporting JSON:API specification in its entirety.

It was a dead end.

Innovation

It lead us to a point where we decided that maybe it is a good idea to create something of our own. We had a simple goal: our Rails application must read JSON:API compliant input and handle it. It would be the best if it did not require huge configuration overhead and if it didn’t force people to learn and write code in a very different (compared to what is acknowledged as standard, Rails-way) convention.

The input that JSON:API specification describes is pretty simple. Take a look at a sample:

POST /photos HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
"data": {
"type": "photos",
"attributes": {
"title": "Ember Hamster",
"src": "http://example.com/images/productivity.png"
},
"relationships": {
"photographer": {
"data": { "type": "people", "id": "9" }
}
}
}
}

Lets break down and note a few things:

  • Content-Type — JSON:API has its own mime type registered: application/vnd.api+json, and specification requires all requests to provide it,
  • Request body. We can see that the main key is data, which holds resources informations.
  • Resource — it is identified by a type, attributes and optional relationships (which, again, are resources).

This input will not work with what you would typically write inside a Rails controller to handle parameters. Instead, this is how it would look like:

{
"photo_parameters": {
"title": "Ember Hamster",
"src": "http://example.com/images/productivity.png",
"photographer": { "id": 9 }
}
}

For a person that had been working with Rails over the years, but not with JSON:API, I would not be surprised if you were in favour of the latter example. However, JSON:API provides lots of great functionalities and features that would not be so easy to achieve on your own (and then, you would have to educate your consumers on how to use these).

At Visuality the choice was made that JSON:API is the way we want to go for now — as it was either that, or the implemented-here solutions that varied in features between different projects. JSON:API gave us a good, unified solution that had every scenario that we needed. Returning data in this format was easy, as you had ActiveModelSerializers or fast_jsonapi from Netflix, but there was no way to easily understand the input client would send us.
But since we already could compare the JSON:API compliant input and the Rails expected one, why not get the best out of two worlds? Why should not we let Rails developers keep using their parameters as they always had done, while allowing JSON:API consumers to actually consume and feed the server with JSON:API data?

JsonApi::Parameters

That is why we brought a new library to life. Our gem aims to support Rails developers in consuming JSON:API input. It is mostly just a translator, that transforms standard compliant input to what Rails (and Rails developers) expect. Considering a sample params call in a controller:

params.require(:photo).permit(photographer: [:id])

The only thing you need to consume the JSON:API version of it is prepend require with from_jsonapi:

params.from_jsonapi.require(:photo).permit(photographer: [:id])

What this function does is literally translating the JSON:API input into a new instance of ActionController::Parameters that has a form of standard Rails model parameters.
The specification has things that may be considered quirks, especially when it comes to creating new related resources — where you have relationships key, and included key (one holds the resource identifier and its type, second holds the resources attributes object). We have done our best to handle these as well.

We did not want to limit our users to Rails only, so you can also use this gem in vanilla Ruby.

Additionally, if you remember from the things we have noted down above, there was this specific JSON:API Content-Type. JsonApi::Parameters registers this mime type for you, in a way that should not break your Rails app (obviously you can unregister the Mime Type yourself, if you would like to).

So far we have been using this library in production for a couple of months now and it has proven handy, so we are looking forward to maintain and extend it. Actually, we have already had a couple of feature requests (one being the Mime::Type registration process) that we were happy to handle and implement, so definitely share your insights with us in the Github repository!

If you’re interested in more blog posts like this please visit our Visuality Blog or our Medium Page

Resources

--

--