An Introduction to the JSON:API Specification

E01ec1de2f7783812d2235a6a9aaaeea?s=47 Dan Gebhardt
October 16, 2019
410

An Introduction to the JSON:API Specification

Presented at the API Specifications Conference in Vancouver, BC.

E01ec1de2f7783812d2235a6a9aaaeea?s=128

Dan Gebhardt

October 16, 2019
Tweet

Transcript

  1. An introduction to Dan Gebhardt (@dgeb) Cerebris Corporation

  2. None
  3. None
  4. None
  5. "A Specification for Building APIs in JSON"

  6. "A Specification for Fetching and Mutating a Graph of Data"

  7. "A representation of resources and their relationships" Graph

  8. application/vnd.api+json

  9. Yehuda Katz @wycats Editors Dan Gebhardt @dgeb Gabe Sullice @gabesullice

  10. History • 2013 - Initial draft released by Yehuda Katz

    • 2015 - v1.0 released • 2018 - v1.1 draft released • 2019 - v1.1 progress continues ...
  11. None
  12. None
  13. None
  14. None
  15. None
  16. "The JSON:API Specification" or "jsonapi.org"

  17. None
  18. None
  19. " " " "

  20. application/vnd.api+json

  21. application/vnd.api+json

  22. application/vnd.[org]+json

  23. Sample of organizations that have either public APIs or public

    OSS projects that support JSON:API application/vnd.[org]+json
  24. Sample of organizations that have either public APIs or public

    OSS projects that support JSON:API application/vnd.api+json
  25. application/vnd.hal+json application/vnd.siren+json application/vnd.collection+json

  26. application/???+json

  27. None
  28. Benefits

  29. Benefits • Shared conventions • Shared tooling • Standards-based best

    practices • Plays well with others • Gradual adoption
  30. Benefits • Shared conventions • Shared tooling • Standards-based best

    practices • Plays well with others • Gradual adoption
  31. Shared conventions

  32. Document structure + Processing rules Shared conventions

  33. Document structure

  34. { "data": {} | [{}] | null, "included": {}, "links":

    {}, "meta": {}, "jsonapi": {}, "errors": [{}] } Top-level structure
  35. { "data": {} | [{}] | null, "included": {}, "links":

    {}, "meta": {}, "jsonapi": {}, "errors": [{}] } Top-level structure }All optional Specific combinations disallowed
  36. { "data": {} | [{}] | null } Top-level structure

  37. { "data": {} | [{}] | null, "included": {}, "links":

    {}, "meta": {} } Top-level structure
  38. { "errors": [{}] } Top-level structure

  39. Primary data { "data": {} | [{}] | null }

  40. { "data": { "type": "article", "id": "123", "attributes": { "title":

    "Introduction to JSON:API", "published": "2019-10-11" } } }
  41. { "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" } }] }
  42. { "type": "article", "id": "123" } Resource object

  43. { "type": "article", "id": "123" } Resource object }Also called

    a "Resource Identity Object" in its simplest form
  44. { "type": "article", "id": "123", "attributes": { // ... this

    article's attributes }, "relationships": { // ... this article's relationships }, "links": { // ... links to this article }, "meta": { // ... metadata about this article } }
  45. { "type": "article", "id": "123", "attributes": { "title": "Hello ASC

    2019!" }, "relationships": { "author": { "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" } } } }
  46. { "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" } } Relationship

    object
  47. { "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" }, "meta": {

    "created": "2019-10-15T18:13:25Z" } } Relationship object
  48. { "links": { "self": "/articles/123/relationships/author", "related": "/articles/123/author" }, "meta": {

    "created": "2019-10-15T18:13:25Z" }, "data": { "type": "person", "id": "123" } } Relationship object
  49. { "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"
  50. { "data": [{ "type": "article", "id": "123", "relationships": { "author":

    { "data": { "type": "person", "id": "abc" } } } }], "included": [{ "type": "person", "id": "abc", "attributes": { "name": "Dan Gebhardt" } }] } Compound Document
  51. { "data": [{ "type": "article", "id": "123", "relationships": { "author":

    { "data": { "type": "person", "id": "abc" } } } }], "included": [{ "type": "person", "id": "abc", "attributes": { "name": "Dan Gebhardt" } }] } Compound Document
  52. { "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" } } } } ] }
  53. { "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" } } } } ] }
  54. Links { "links": { "self": "/articles/123" } }

  55. Links { "links": { "self": "/articles/123", "doSomething": { "href": "/articles/123/doSomething",

    "meta": { "note": "POST to do something" } } } }
  56. 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
  57. { "meta": { "whatever": "you want", "can": { "go": "in

    meta" } } } Metadata
  58. { "meta": { "whatever": "you want", "can": { "go": "in

    meta" } } } Metadata }Allowed metadata locations: • Top-level • Resource objects • Resource identity objects • Link objects • Error objects
  59. Document structure + Processing rules Shared conventions

  60. Processing rules

  61. Opinionated HTTP usage

  62. Opinionated HTTP usage • GET • POST • PATCH •

    DELETE • Resources • Relationships
  63. • GET • POST • PATCH • DELETE • Resources

    • Relationships Opinionated HTTP usage
  64. • GET • POST • PATCH • DELETE • Resources

    • Relationships Opinionated HTTP usage
  65. Fetching GET /articles GET /articles/123 Note: URL Structure is not

    dictated by JSON:API
  66. Graph Fetching GET /articles?include=author GET /articles?include=comments,author GET /articles?include=comments.author,author

  67. Sparse Fieldsets GET /articles?fields=title,author GET /articles?include=author& fields[article]=title,author& fields[person]=name

  68. Sorting GET /people?sort=age GET /people?sort=age,name GET /people?sort=-lastUpdated,name

  69. Pagination GET /people?page[???]=x }Pagination strategy agnostic: • Page-based • Cursor-based

  70. Filtering GET /people?filter[???]=x }Filtering strategy agnostic: • Simple, strict match

    • Nested conditions • Vertical-specific filtering
  71. • GET • POST • PATCH • DELETE • Resources

    • Relationships Opinionated HTTP usage Documented at jsonapi.org
  72. Benefits • Shared conventions • Shared tooling • Standards-based best

    practices • Plays well with others • Gradual adoption
  73. Shared tooling

  74. None
  75. 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
  76. 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
  77. Benefits • Shared conventions • Shared tooling • Standards-based best

    practices • Plays well with others • Gradual adoption
  78. Standards-based best practices

  79. Composition of standards • HTTP (RFC7231) • JSON (RFC8259) •

    URI (RFC3986) • Profiles (RFC6906) • Web linking (RFC8288) • UUID (RFC4122) • And more ...
  80. Composition of standards Evolution with those standards

  81. Composition of standards Benefit from ecosystems that support those standards

  82. Benefits • Shared conventions • Shared tooling • Standards-based best

    practices • Plays well with others • Gradual adoption
  83. Plays well with others • OpenAPI • JSON Schema •

    JSON-LD • And more ...
  84. Benefits • Shared conventions • Shared tooling • Standards-based best

    practices • Plays well with others • Gradual adoption
  85. You MAY use feature X. Gradual adoption If you detect

    feature X, you MUST do A, B, and C.
  86. Source: https://martinfowler.com/articles/richardsonMaturityModel.html Gradual adoption

  87. JSON:API v1.1

  88. JSON:API v1.1 • Profiles • Extensions • Expanded hypermedia controls

  89. Profiles Extensions Specify meaning for members already reserved for users.

    Specify meaning for members reserved for the spec itself.
  90. Profiles Extensions Negotiated with the `profile` media type parameter. Negotiated

    with the `ext` media type parameter.
  91. Profiles Extensions May be ignored by servers. Must be supported

    or else error.
  92. 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
  93. 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
  94. { "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
  95. Expanded hypermedia controls

  96. None
  97. Beyond JSON:API v1.1

  98. Beyond JSON:API v1.1 • Optimize for HTTP/2/3 • More extensions!

    More experiments! • Focus on DX via tooling (e.g. spectral rulesets)
  99. HTTP/1.1 Includes GET /articles?include=author GET /articles?include=comments,author GET /articles?include=comments.author,author

  100. HTTP/2 Server Push GET /articles?serverPush=author GET /articles?serverPush=comments,author GET /articles?serverPush=comments.author,author Experimental

  101. HTTP/2 Server Push Experimental

  102. Resources

  103. None
  104. None
  105. None
  106. None
  107. None
  108. An introduction to Dan Gebhardt (@dgeb) Cerebris Corporation

  109. An introduction to Dan Gebhardt (@dgeb) Cerebris Corporation Thanks! jsonapi.org

    @jsonapi