Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
An Introduction to the JSON:API Specification
Search
Dan Gebhardt
October 16, 2019
5
680
An Introduction to the JSON:API Specification
Presented at the API Specifications Conference in Vancouver, BC.
Dan Gebhardt
October 16, 2019
Tweet
Share
More Decks by Dan Gebhardt
See All by Dan Gebhardt
Worker power!
dgeb
0
450
Modern Ember
dgeb
0
130
The Future of Data in Ember
dgeb
0
410
Give Apps Online Superpowers by Optimizing them for Offline
dgeb
2
190
Overview of Orbit.js
dgeb
0
88
Introducing Ember Engines
dgeb
4
3.4k
Introducing JSON API
dgeb
5
680
Fault Tolerant UX
dgeb
4
910
Ambitious Data Flows with Ember.js and Orbit.js
dgeb
10
1.6k
Featured
See All Featured
The World Runs on Bad Software
bkeepers
PRO
65
11k
A designer walks into a library…
pauljervisheath
204
24k
Speed Design
sergeychernyshev
25
670
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
GraphQLとの向き合い方2022年版
quramy
44
13k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.6k
Code Reviewing Like a Champion
maltzj
520
39k
How GitHub (no longer) Works
holman
311
140k
Music & Morning Musume
bryan
46
6.2k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.7k
Transcript
An introduction to Dan Gebhardt (@dgeb) Cerebris Corporation
None
None
None
"A Specification for Building APIs in JSON"
"A Specification for Fetching and Mutating a Graph of Data"
"A representation of resources and their relationships" Graph
application/vnd.api+json
Yehuda Katz @wycats Editors Dan Gebhardt @dgeb Gabe Sullice @gabesullice
History • 2013 - Initial draft released by Yehuda Katz
• 2015 - v1.0 released • 2018 - v1.1 draft released • 2019 - v1.1 progress continues ...
None
None
None
None
None
"The JSON:API Specification" or "jsonapi.org"
None
None
" " " "
application/vnd.api+json
application/vnd.api+json
application/vnd.[org]+json
Sample of organizations that have either public APIs or public
OSS projects that support JSON:API application/vnd.[org]+json
Sample of organizations that have either public APIs or public
OSS projects that support JSON:API application/vnd.api+json
application/vnd.hal+json application/vnd.siren+json application/vnd.collection+json
application/???+json
None
Benefits
Benefits • Shared conventions • Shared tooling • Standards-based best
practices • Plays well with others • Gradual adoption
Benefits • Shared conventions • Shared tooling • Standards-based best
practices • Plays well with others • Gradual adoption
Shared conventions
Document structure + Processing rules Shared conventions
Document structure
{ "data": {} | [{}] | null, "included": {}, "links":
{}, "meta": {}, "jsonapi": {}, "errors": [{}] } Top-level structure
{ "data": {} | [{}] | null, "included": {}, "links":
{}, "meta": {}, "jsonapi": {}, "errors": [{}] } Top-level structure }All optional Specific combinations disallowed
{ "data": {} | [{}] | null } Top-level structure
{ "data": {} | [{}] | null, "included": {}, "links":
{}, "meta": {} } Top-level structure
{ "errors": [{}] } Top-level structure
Primary data { "data": {} | [{}] | null }
{ "data": { "type": "article", "id": "123", "attributes": { "title":
"Introduction to JSON:API", "published": "2019-10-11" } } }
{ "data": [{ "type": "article", "id": "123", "attributes": { "title":
"Introduction to JSON:API", "published": "2019-10-11" } }, { "type": "article", "id": "124", "attributes": { "title": "Retrospective on ASC 2019", "published": "2019-10-15" } }] }
{ "type": "article", "id": "123" } Resource object
{ "type": "article", "id": "123" } Resource object }Also called
a "Resource Identity Object" in its simplest form
{ "type": "article", "id": "123", "attributes": { // ... this
article's attributes }, "relationships": { // ... this article's relationships }, "links": { // ... links to this article }, "meta": { // ... metadata about this article } }
{ "type": "article", "id": "123", "attributes": { "title": "Hello ASC
2019!" }, "relationships": { "author": { "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" } } } }
{ "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" } } Relationship
object
{ "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" }, "meta": {
"created": "2019-10-15T18:13:25Z" } } Relationship object
{ "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" }, "meta": {
"created": "2019-10-15T18:13:25Z" }, "data": { "type": "person", "id": "123" } } Relationship object
{ "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" }, "meta": {
"created": "2019-10-15T18:13:25Z" }, "data": { "type": "person", "id": "123" } } Relationship object }Resource identity object(s), or "Linkage data"
{ "data": [{ "type": "article", "id": "123", "relationships": { "author":
{ "data": { "type": "person", "id": "abc" } } } }], "included": [{ "type": "person", "id": "abc", "attributes": { "name": "Dan Gebhardt" } }] } Compound Document
{ "data": [{ "type": "article", "id": "123", "relationships": { "author":
{ "data": { "type": "person", "id": "abc" } } } }], "included": [{ "type": "person", "id": "abc", "attributes": { "name": "Dan Gebhardt" } }] } Compound Document
{ "data": [ { "type": "article", "id": "1", "attributes": {
"title": "JSON:API paints my bikeshed!" }, "relationships": { "author": { "data": { "type": "person", "id": "9" } }, "comments": { "data": [ { "type": "comments", "id": "5" } ] } } } ], // continued ... // ... continued "included": [ { "type": "person", "id": "9", "attributes": { "name": "Dan Gebhardt" } }, { "type": "comments", "id": "5", "attributes": { "body": "First!" }, "relationships": { "author": { "data": { "type": "person", "id": "9" } } } } ] }
{ "data": [ { "type": "article", "id": "1", "attributes": {
"title": "JSON:API paints my bikeshed!" }, "relationships": { "author": { "data": { "type": "person", "id": "9" } }, "comments": { "data": [ { "type": "comments", "id": "5" } ] } } } ], // continued ... // ... continued "included": [ { "type": "person", "id": "9", "attributes": { "name": "Dan Gebhardt" } }, { "type": "comments", "id": "5", "attributes": { "body": "First!" }, "relationships": { "author": { "data": { "type": "person", "id": "9" } } } } ] }
Links { "links": { "self": "/articles/123" } }
Links { "links": { "self": "/articles/123", "doSomething": { "href": "/articles/123/doSomething",
"meta": { "note": "POST to do something" } } } }
Links { "links": { "self": "/articles/123", "doSomething": { "href": "/articles/123/doSomething",
"meta": { "note": "POST to do something" } } } } }Allowed links locations: • Top-level • Resource objects • Relationship objects • Error objects
{ "meta": { "whatever": "you want", "can": { "go": "in
meta" } } } Metadata
{ "meta": { "whatever": "you want", "can": { "go": "in
meta" } } } Metadata }Allowed metadata locations: • Top-level • Resource objects • Resource identity objects • Link objects • Error objects
Document structure + Processing rules Shared conventions
Processing rules
Opinionated HTTP usage
Opinionated HTTP usage • GET • POST • PATCH •
DELETE • Resources • Relationships
• GET • POST • PATCH • DELETE • Resources
• Relationships Opinionated HTTP usage
• GET • POST • PATCH • DELETE • Resources
• Relationships Opinionated HTTP usage
Fetching GET /articles GET /articles/123 Note: URL Structure is not
dictated by JSON:API
Graph Fetching GET /articles?include=author GET /articles?include=comments,author GET /articles?include=comments.author,author
Sparse Fieldsets GET /articles?fields=title,author GET /articles?include=author& fields[article]=title,author& fields[person]=name
Sorting GET /people?sort=age GET /people?sort=age,name GET /people?sort=-lastUpdated,name
Pagination GET /people?page[???]=x }Pagination strategy agnostic: • Page-based • Cursor-based
Filtering GET /people?filter[???]=x }Filtering strategy agnostic: • Simple, strict match
• Nested conditions • Vertical-specific filtering
• GET • POST • PATCH • DELETE • Resources
• Relationships Opinionated HTTP usage Documented at jsonapi.org
Benefits • Shared conventions • Shared tooling • Standards-based best
practices • Plays well with others • Gradual adoption
Shared tooling
None
Server • Swift • PHP • Node.js • Ruby •
Python • Go • .NET • Java • JavaScript • Typescript • iOS • Ruby • PHP • Dart • Perl Client Libraries • Scala • Elixir • Haskell • Perl • Vala • Rust • Dart • Java • Android • R • Elm • .NET • Python • Elixir 74 93
Server netflix/fast_jsonapi google/jsonapi drupal/jsonapi cerebris/jsonapi-resources rails-api/active_model_serializers neomerx/json-api emberjs/data orbitjs/orbit crnk-project/crnk-framework
jsonapi-ios/Spine reststate/Mobx reststate/Vuex Client Libraries
Benefits • Shared conventions • Shared tooling • Standards-based best
practices • Plays well with others • Gradual adoption
Standards-based best practices
Composition of standards • HTTP (RFC7231) • JSON (RFC8259) •
URI (RFC3986) • Profiles (RFC6906) • Web linking (RFC8288) • UUID (RFC4122) • And more ...
Composition of standards Evolution with those standards
Composition of standards Benefit from ecosystems that support those standards
Benefits • Shared conventions • Shared tooling • Standards-based best
practices • Plays well with others • Gradual adoption
Plays well with others • OpenAPI • JSON Schema •
JSON-LD • And more ...
Benefits • Shared conventions • Shared tooling • Standards-based best
practices • Plays well with others • Gradual adoption
You MAY use feature X. Gradual adoption If you detect
feature X, you MUST do A, B, and C.
Source: https://martinfowler.com/articles/richardsonMaturityModel.html Gradual adoption
JSON:API v1.1
JSON:API v1.1 • Profiles • Extensions • Expanded hypermedia controls
Profiles Extensions Specify meaning for members already reserved for users.
Specify meaning for members reserved for the spec itself.
Profiles Extensions Negotiated with the `profile` media type parameter. Negotiated
with the `ext` media type parameter.
Profiles Extensions May be ignored by servers. Must be supported
or else error.
POST /bulk HTTP/1.1 Host: example.org Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic" Accept: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic" {
"atomic:operations": [{ "op": "add", "href": "/blogPosts", "data": { "type": "articles", "attributes": { "title": "JSON API paints my bikeshed!" } } }] } Extension: Atomic Operations Experimental
HTTP/1.1 200 OK Content-Type: application/vnd.api+json;ext="https://jsonapi.org/ext/atomic" { "atomic:results": [{ "data": {
"links": { "self": "http://example.com/blogPosts/13" }, "type": "articles", "id": "13", "attributes": { "title": "JSON API paints my bikeshed!" } } }] } Extension: Atomic Operations Experimental
{ "atomic:operations": [{ "op": "add", "data": { "type": "authors", "id":
"acb2ebd6-ed30-4877-80ce-52a14d77d470", "attributes": { "name": "dgeb" } } }, { "op": "add", "data": { "type": "articles", "id": "bb3ad581-806f-4237-b748-f2ea0261845c", "attributes": { "title": "JSON API paints my bikeshed!" }, "relationships": { "author": { "data": { "type": "authors", "id": "acb2ebd6-ed30-4877-80ce-52a14d77d470" } } } } }] } }Multiple linked operations Experimental
Expanded hypermedia controls
None
Beyond JSON:API v1.1
Beyond JSON:API v1.1 • Optimize for HTTP/2/3 • More extensions!
More experiments! • Focus on DX via tooling (e.g. spectral rulesets)
HTTP/1.1 Includes GET /articles?include=author GET /articles?include=comments,author GET /articles?include=comments.author,author
HTTP/2 Server Push GET /articles?serverPush=author GET /articles?serverPush=comments,author GET /articles?serverPush=comments.author,author Experimental
HTTP/2 Server Push Experimental
Resources
None
None
None
None
None
An introduction to Dan Gebhardt (@dgeb) Cerebris Corporation
An introduction to Dan Gebhardt (@dgeb) Cerebris Corporation Thanks! jsonapi.org
@jsonapi