14
.
11
.
2023
13
.
04
.
2018
Backend
Frontend
Tutorial

JSON API versus the NIH syndrome

Nadia Miętkiewicz
Frontend Developer

Not Invented Here (NIH) Syndrome is the tendency of organizations to reject suitable existing solutions in favor of dedicated, internally-developed ones, believing that the use-cases they face are so unique that no external solution could ever fit their needs. The result of this belief in being a special snowflake is often wasting hours on reinventing the wheel and later even more time needed for onboarding each new team member with the intricacies of each custom solution.

In many projects, one the first important architectural decision is the API design. The shape of the API requests and responses will define the way frontend and backend communicate with each other and will serve as a contract between those two parties throughout the project. Given the gravity of this choice, countless meetings and discussions are held over the topic of what convention and formatting to adopt and almost always there are as many opinions as there are developers.

Introducing JSON API

JSON API is a light and extendable specification for how the RESTful API requests and responses should be formatted. It's a proposition based on the combined outcome of many such design meetings already held. It's not an implementation, nor a ready-to-go framework, just a set of written rules and suggestions to adhere to. The importance or optionality of each rule is specified with the key words "MUST", "SHOULD", "RECOMMENDED", "MAY" etc.

It has been around since 2015 in its stable version and has it's own registered mime-type Content-Type: application/vnd.api+json to be used by both the client and server to exchange data.

Example of a JSON API response

{
  "data": [{
    "type": "posts",
    "id": "1",
    "attributes": {
      "title": "JSON API versus the NIH syndrome",
      "excerpt": "Why JSON API is good for you",
      "contents": "JSON API is a light and extendable specification for how the RESTful API requests and responses should be formatted...",
      "date": "2018-04-09T12:00:00.000Z"
    },
    "relationships": {
      "author": {
        "data": {"id": "2", "type": "people"}
      },
      "comments": {
        "data": {"id": "3", "type": "comments"},
        "data": {"id": "4", "type": "comments"}
      }
    }
  }]
}

JSON API aims to standardize the distinction between resource object's data attributes and its relationships with other resources, hence the type and id parameters are mandatory for each resource object, as they are used to connect various resources.

Sparse fieldsets

The above example may seem too verbose at first but the beauty of the JSON API is the optimization possibilities it offers. The main strategy is to have the endpoint include all the data available by default but to allow gradually cutting it down by specifying the needed fields in the request parameters.

GET /posts?fields[posts]=title,excerpt

{
  "data": [{
    "type": "posts",
    "id": "1",
    "attributes": {
      "title": "JSON API versus the NIH syndrome",
      "excerpt": "Why JSON API is good for you"
    }
  }]
}

This way the amount of data returned can be configured per-request, and any decision changes concerning which information should be displayed on the frontend side can happen without having to add or remove any fields in the backend implementation of the endpoint.

Optimizing the number of requests

Whereas the fields parameter allowed to reduce the amount of data sent, the include parameter aims to expand it.

GET /articles/1?include=author&fields[posts]=title,excerpt&fields[people]=firstName

{
  "data": [{
    "type": "posts",
    "id": "1",
    "attributes": {
      "title": "JSON API versus the NIH syndrome",
      "excerpt": "Why JSON API is good for you"
    },
    "relationships": {
      "author": {
        "data": {"id": "2", "type": "people"}
      }
    }
  }],
  "included": [
    {
      "type": "people",
      "id": "2",
      "attributes": {
        "firstName": "Nadia"
      }
    }
  ]
}

By specifying which of the related resource objects should be fetched in the same request we can avoid cascading requests (GET /posts/1 then GET /posts/1/relationships/author).

Again this gives an almost GraphQL level of configuration power to the request maker, without the need of fully replacing the HATEOAS REST API and can be used as an introduction to some key concepts of GraphQL.

Filtering, sorting, and pagination

When it comes to handling the basic operations on multipage or categorized resource objects JSON API specifies a unified way of doing so, by using the page, sort and filter parameters, the last of which is pretty vague and leaves much freedom to the interpretation.

GET /posts?fields[posts]=title,date&page[number]=3&page[size]=2&sort=date

{
  "meta": {
    "total-pages": 10
  },
  "data": [
    {
      "type": "posts",
      "id": "1",
      "attributes": {
        "title": "Some title",
        "date": "2018-04-09T12:00:00.000Z"
      }
    },
    {
      "type": "posts",
      "id": "2",
      "attributes": {
        "title": "Some other title",
        "date": "2018-04-07T14:00:00.000Z"
      }
    }
  ],
  "links": {
    "prev": "/posts?page[number]=2&page[size]=2",
    "next": "/posts?page[number]=4&page[size]=2"
  }
}

The response can include a meta field, which can be used to fetch any extra data that doesn't represent a resource object, and the links field which represent the application links or api endpoints that can be queried for related information.

Implementations

JSON API itself is language- and implementation- agnostic. There are however a number of existing solutions that implement this standard, listed on http://jsonapi.org/implementations/. Including them in your project can make switching to JSON API painless and leave you free of API design woes and with more time to focus on the actual domain-specific problems of your projects.

Nadia Miętkiewicz
Frontend Developer

Check my Twitter

Check my Linkedin

Did you like it? 

Sign up To VIsuality newsletter

READ ALSO

How to become a Ruby Certified Programmer Title image

How to become a Ruby Certified Programmer

14
.
11
.
2023
Michał Łęcicki
Ruby
Visuality
Vector Search in Ruby - Paweł Strzałkowski

Vector Search in Ruby

17
.
03
.
2024
Paweł Strzałkowski
ChatGPT
Embeddings
Postgresql
Ruby
Ruby on Rails
LLM Embeddings in Ruby - Paweł Strzałkowski

LLM Embeddings in Ruby

17
.
03
.
2024
Paweł Strzałkowski
Ruby
LLM
Embeddings
ChatGPT
Ollama
Handling Errors in Concurrent Ruby, Michał Łęcicki

Handling Errors in Concurrent Ruby

14
.
11
.
2023
Michał Łęcicki
Ruby
Ruby on Rails
Tutorial
Recap of Friendly.rb 2024 conference

Insights and Inspiration from Friendly.rb: A Ruby Conference Recap

02
.
10
.
2024
Kaja Witek
Conferences
Ruby on Rails

Covering indexes - Postgres Stories

14
.
11
.
2023
Jarosław Kowalewski
Ruby on Rails
Postgresql
Backend
Ula Sołogub - SQL Injection in Ruby on Rails

The Deadly Sins in RoR security - SQL Injection

14
.
11
.
2023
Urszula Sołogub
Backend
Ruby on Rails
Software
Michal - Highlights from Ruby Unconf 2024

Highlights from Ruby Unconf 2024

14
.
11
.
2023
Michał Łęcicki
Conferences
Visuality
Cezary Kłos - Optimizing Cloud Infrastructure by $40 000 Annually

Optimizing Cloud Infrastructure by $40 000 Annually

14
.
11
.
2023
Cezary Kłos
Backend
Ruby on Rails

Smooth Concurrent Updates with Hotwire Stimulus

14
.
11
.
2023
Michał Łęcicki
Hotwire
Ruby on Rails
Software
Tutorial

Freelancers vs Software house

02
.
10
.
2024
Michał Krochecki
Visuality
Business

Table partitioning in Rails, part 2 - Postgres Stories

14
.
11
.
2023
Jarosław Kowalewski
Backend
Postgresql
Ruby on Rails

N+1 in Ruby on Rails

14
.
11
.
2023
Katarzyna Melon-Markowska
Ruby on Rails
Ruby
Backend

Turbo Streams and current user

29
.
11
.
2023
Mateusz Bilski
Hotwire
Ruby on Rails
Backend
Frontend

Showing progress of background jobs with Turbo

14
.
11
.
2023
Michał Łęcicki
Ruby on Rails
Ruby
Hotwire
Frontend
Backend

Table partitioning in Rails, part 1 - Postgres Stories

14
.
11
.
2023
Jarosław Kowalewski
Postgresql
Backend
Ruby on Rails

Table partitioning types - Postgres Stories

14
.
11
.
2023
Jarosław Kowalewski
Postgresql
Backend

Indexing partitioned table - Postgres Stories

14
.
11
.
2023
Jarosław Kowalewski
Backend
Postgresql
SQL Views in Ruby on Rails

SQL views in Ruby on Rails

14
.
11
.
2023
Jan Grela
Backend
Ruby
Ruby on Rails
Postgresql
Design your bathroom in React

Design your bathroom in React

14
.
11
.
2023
Bartosz Bazański
Frontend
React
Lazy Attributes in Ruby - Krzysztof Wawer

Lazy attributes in Ruby

14
.
11
.
2023
Krzysztof Wawer
Ruby
Software

Exporting CSV files using COPY - Postgres Stories

14
.
11
.
2023
Jarosław Kowalewski
Postgresql
Ruby
Ruby on Rails
Michał Łęcicki - From Celluloid to Concurrent Ruby

From Celluloid to Concurrent Ruby: Practical Examples Of Multithreading Calls

14
.
11
.
2023
Michał Łęcicki
Backend
Ruby
Ruby on Rails
Software