Automatic JSON API Deserialization in Rails

- 4 mins

JSON API is a great standard for designing APIs without going through the same bikeshedding headakes every API designer usually has to (representing polymorphic associations, duplicated associated records, etc.). Since the spec has converged to a stable version, it has gained a strong support, especially in the rails community.1 However, most libraries are aimed towards serialization (i.e. generating JSON API compliant JSON), not deserialization (i.e. parsing a JSON API payload into a more ready-to-use format). Implementers are sort of left to do their own deserialization (for POST requests for instance). We addressed that in ActiveModelSerializers by providing helper methods, although it never felt like it really belonged there.

I created the jsonapi gem to take care of the parsing and validating part of handling JSON API payloads,2 and moved the deserialization logic there, before removing it as it did not really belong there either.

Finally, I took advantage of JSON API defining its own MIME type (the rails implications of which are described in Benjamin Fleischer’s blog) to issue a PR to rails, so that deserialization happens at the earliest possible time, making the implementer able to use the params as if they were supplied via plain JSON or multipart data. That is, upon receiving the following payload:

  "data": {
    "type": "posts",
    "attributes" : {
      "title": "Hello",
      "date": "today"
    "relationships": {
      "author": { "data": { "id": "2", "type": "users" } },
      "comments": {
        "data": [
          { "type": "comments", "id": "3" },
          { "type": "comments", "id": "4" }
      "journal": { "data": null }

the value of params within the controller would be:

  "_type" => "posts",
  "title" => "Hello",
  "date" => "today",
  "author_id" => "2",
  "author_type" => "User",
  "comment_ids" => ["3", "4"],
  "comment_types" => ["Comment", "Comment"],
  "journal_id" => nil

Basically, the changes amount to:

  • registering the application/vnd.api+json MIME type:
Mime::Type.register "application/vnd.api+json", :jsonapi
  • declaring a JSON API parameter parser:
jsonapi_parser = -> (json) {
  json = ActiveSupport::JSON.decode(json)
  data = ::JSONAPI.parse(json).data
  return {} if data.nil?
  hash = {}
  hash["id"] = unless
  hash["_type"] = data.type
  data.relationships.each do |name, rel|
      hash["#{name.singularize}_ids"] =
      hash["#{name.singularize}_types"] = { |val| val.type.singularize.capitalize }
    elsif !
      hash["#{name}_id"] =
      hash["#{name}_type"] =
      hash["#{name}_id"] = nil
  • registering the parameter parser:
ActionDispatch::Request.parameter_parsers.merge!(Mime[:jsonapi].symbol => jsonapi_parser)
  1. ActiveModelSerializers and JSONAPI::Resources both have nearly full support.

  2. A simple JSON schema is not sufficient here, as the format cannot be fully described as such (for instance the full linkage property).

Lucas Hosseini

Lucas Hosseini

Coder with attitude.

comments powered by Disqus
rss facebook twitter github youtube mail spotify instagram linkedin google pinterest medium