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
Practical Orchestrator
shlominoach
186
10k
Designing for Performance
lara
604
68k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Fontdeck: Realign not Redesign
paulrobertlloyd
82
5.3k
It's Worth the Effort
3n
183
28k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Build The Right Thing And Hit Your Dates
maggiecrowley
33
2.4k
Rails Girls Zürich Keynote
gr2m
94
13k
Bootstrapping a Software Product
garrettdimon
PRO
305
110k
Optimising Largest Contentful Paint
csswizardry
33
3k
Testing 201, or: Great Expectations
jmmastey
40
7.1k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.1k
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