Introduction to the JSON API


October 18th, 2018


JSON API was originally drafted in May 2013 by Yehuda Katz and reached stable in May 2015, and it is about making your API calls efficient. You can fetch data as you need, adding or removing attributes or relations as your requirements change. This minimizes the amount of data and round trips needed when making API calls.


JSON API documents work as any other API format, you send a request to an endpoint and receive your document. The JSON API specification defines how resources are structured. This structure helps in normalizing how you consume the API.

For example when you make a call to the resource articles through a simple GET request.

1GET /articles HTTP/1.1
2Accept: application/vnd.api+json

You receive a response which should look something like this:

1// ...
3 "type": "articles",
4 "id": "1",
5 "attributes": {
6 "title": "Rails is Omakase"
7 },
8 "relationships": {
9 "author": {
10 "links": {
11 "self": "",
12 "related": ""
13 },
14 "data": {
15 "type": "people",
16 "id": "9"
17 }
18 }
19 },
20 "links": {
21 "self": ""
22 }
23} // ...

Seems simple enough. The author relationship includes a link to the relationship itself and some basic information about the relation. Using the links in the document you can retrieve information on the related resources.

But this is only scratching the surface. There are a quite a few features which make JSON API so lovely to use.

Compound documents

To reduce the number of HTTP requests, servers MAY allow responses that include related resources along with the requested primary resources. Such responses are called “compound documents”.

A compound document is a resource which includes the data of included relations. For example, when requesting an article as shown earlier it may include the data of the author so you don’t need a second call to fetch the author of the article.

This is awesome for a few reasons. Getting the full data of a resource you want to consume in one go is unheard of unless specific endpoints are programmed. On the server side of things, caching and invalidating a request like this is easy.

Just have a look at this example which returns a set of 1 document.

2 "data": [{
3 "type": "articles",
4 "id": "1",
5 "attributes": {
6 "title": "JSON API paints my bikeshed!"
7 },
8 "links": {
9 "self": ""
10 },
11 "relationships": {
12 "author": {
13 "links": {
14 "self": "",
15 "related": ""
16 },
17 "data": {
18 "type": "people",
19 "id": "9"
20 }
21 },
22 "comments": {
23 "links": {
24 "self": "",
25 "related": ""
26 },
27 "data": [{
28 "type": "comments",
29 "id": "5"
30 }, {
31 "type": "comments",
32 "id": "12"
33 }]
34 }
35 }
36 }],
37 "included": [{
38 "type": "people",
39 "id": "9",
40 "attributes": {
41 "first-name": "Dan",
42 "last-name": "Gebhardt",
43 "twitter": "dgeb"
44 },
45 "links": {
46 "self": ""
47 }
48 }, {
49 "type": "comments",
50 "id": "5",
51 "attributes": {
52 "body": "First!"
53 },
54 "relationships": {
55 "author": {
56 "data": {
57 "type": "people",
58 "id": "2"
59 }
60 }
61 },
62 "links": {
63 "self": ""
64 }
65 }, {
66 "type": "comments",
67 "id": "12",
68 "attributes": {
69 "body": "I like XML better"
70 },
71 "relationships": {
72 "author": {
73 "data": {
74 "type": "people",
75 "id": "9"
76 }
77 }
78 },
79 "links": {
80 "self": ""
81 }
82 }]

If you look at the included property you see all the related resources for the article. Every included document has a type property which defines which type of resource is returned and a link to the endpoint for the document.

It might feel weird to refer to the author relation in the article only by id and type and loading the related people resource from the included tag. But imagine when there are multiple articles referring to the same author, the document only includes the referred people once in the included data. Pretty efficient!

Inclusion of related resources

In the above example, the server includes all relations in the response, but you might not want this by default since this bloats the responses a bit. For this reason, the specification provides a way to specify what relations should be included in the response.

For example, if you only need the author relationship included you can just call the endpoint with the include request parameter. This will tell the server to only send the specified relation with the response.

1GET /articles/1?include=author HTTP/1.1
2Accept: application/vnd.api+json

If you need multiple relationships you separate the relations with a comma (,).
When defining the includes you can go even further and include nested relations. For example, you want comments, with the authors of the comments you can just include

1GET /articles/1?include=author, HTTP/1.1
2Accept: application/vnd.api+json

This flexibility makes fetching the correct resources a breeze allowing you to tweak the results as needed.

Sparse fieldsets

When you are using compound documents your request could get large, fast. Especially when the included relationships contain a lot of data. Most of the time you do not need every single attribute defined in the resources but only want things like author names. JSON API provides sparse fieldsets for this use case.

You can specify the fields that are fetched by setting the fields request parameter. The format is fields[TYPE], so you can specify per resource type what fields you need.

1GET /articles?include=author&fields[articles]=title,body&fields[people]=name HTTP/1.1
2Accept: application/vnd.api+json

This call could include the title and body field from the articles resource and the name field from the people resource.

Other features

Servers may implement a few more features which are defined by the specification. These features are includes, sorting, pagination and filtering.


If the server supports it, you can sort your records using the sort request parameter. Sorting is in ascending order by default, to sort descending you can prepend - to the field.

1GET /articles?sort=-created,title HTTP/1.1
2Accept: application/vnd.api+json


If the server supports pagination it will supply a few extra meta attributes with pagination information. The server can decide how to paginate themselves, be it through an offset, or direct page numbers. An example of an implementation uses the required links to other pages and includes some metadata in the meta tag.

2 links: {
3 first: "",
4 last: "",
5 prev: "",
6 next: null
7 },
8 meta: {
9 current_page: 262,
10 from: 3916,
11 last_page: 262,
12 per_page: 15,
13 to: 3924,
14 total: 3924
15 }


The spec says little about filtering, it just states there may be a parameter which is called filter which is used to fulfil filtering on the server. The implementation is specific for the server and can be anything.

Creating, Updating and Deleting resources

When you understand the structure of documents, creating and updating resources is a breeze. It uses the standard HTTP verbs to communicate the desired action of the request. GET for fetching, POST for creating, PATCH for (partial) updating and DELETE for deleting resources.

Creating resources

1POST /photos HTTP/1.1
2Content-Type: application/vnd.api+json
3Accept: application/vnd.api+json
6 "data": {
7 "type": "photos",
8 "attributes": {
9 "title": "Ember Hamster",
10 "src": ""
11 },
12 "relationships": {
13 "photographer": {
14 "data": { "type": "people", "id": "9" }
15 }
16 }
17 }

This is an example of posting a new photo. As you notice the relationship is included in the request in the relationships property.

Updating resources

1PATCH /articles/1 HTTP/1.1
2Content-Type: application/vnd.api+json
3Accept: application/vnd.api+json
6 "data": {
7 "type": "articles",
8 "id": "1",
9 "attributes": {
10 "title": "To TDD or Not"
11 }
12 }

When updating a resource it will update the posted fields to the new values. Values that are not included in the request will not be updated.

Updating relationships

It is possible to update relationships in 2 ways. One way is to include the relation in the PATCH request. The same way as you have seen earlier. Another way is using the specific relationship endpoint.

1PATCH /articles/1/relationships/author HTTP/1.1
2Content-Type: application/vnd.api+json
3Accept: application/vnd.api+json
6 "data": { "type": "people", "id": "12" }

This call will update the to-one relationship on the article. If you want to delete the relation, you can pass null as the data.

If you want to update a to-many relation you can just send an array of relations to the endpoint. This will replace all members in the relation with the data you post.

1PATCH /articles/1/relationships/tags HTTP/1.1
2Content-Type: application/vnd.api+json
3Accept: application/vnd.api+json
6 "data": [
7 { "type": "tags", "id": "2" },
8 { "type": "tags", "id": "3" }
9 ]

When clearing a to-many relation, just send an empty array as data.

Deleting resources

There isn’t much to say about deleting resources, you just send a DELETEcall to the endpoint.

1DELETE /photos/1 HTTP/1.1
2Accept: application/vnd.api+json

Learn more

If you want to learn more about the details of {json:api} and its usage, check out Their specification is quite clear, although there are a lot of features that are not mandatory for servers to implement. On their site, they also maintain a list of client and server implementations for you to get up and running quickly.

They are currently working on version 1.1 of the specification. It will be 100% backwards compatible with version 1.0, only adding new features, breaking nothing.

{json:api} <3

Hopefully, you’ve learned what makes this specification awesome. Personally, I love the structure, consistency and flexibility, this makes communicating with an API a lot easier. You always know what to expect, and don’t need loads of requests to get your data.

For this reason we have been working on a Laravel package for mapping remote {json:api} resources to Eloquent like models and collections, and a package to generate an {json:api} from your Eloquent models.

Filed in:

Björn Brala

Technical director at SWIS, on a quest to make the internet a better place through open source.

Laravel News Partners