Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Getting to JSON:API: What it is, why you should use it, and how to use it in Laravel

Matt Stauffer
February 26, 2020

Getting to JSON:API: What it is, why you should use it, and how to use it in Laravel

Getting to JSON:API: What it is, why you should use it, and how to use it in Laravel

Matt Stauffer

February 26, 2020
Tweet

More Decks by Matt Stauffer

Other Decks in Technology

Transcript

  1. GETTING TO JSON:API WHAT IT IS, WHY YOU SHOULD USE

    IT, and HOW TO USE IT IN LARAVEL Getting to JSON:API - Matt Stauffer
  2. First half: Theory ▸ 1. Intro to REST APIs ▸

    2. The State of APIs ▸ 3. What is JSON:API? ▸ 4. Why JSON:API? Getting to JSON:API - Matt Stauffer
  3. Second half: Implementation ▸ 5. JSON:API Tooling in Laravel ▸

    6. Other API concerns ▸ 7. After the talk ▸ 8. Outro Getting to JSON:API - Matt Stauffer
  4. REST: CRUD over JSON using HTTP verbs shaped like /dogs/14

    and stuff Getting to JSON:API - Matt Stauffer
  5. REST is: ▸ Client/server ▸ Stateless ▸ Cacheable ▸ Layered

    (proxyable, load balanceable) ▸ Uniform Interface: ▸ Resource identification in requests ▸ Resource manipulation through representations ▸ Self-descriptive messages (content headers) ▸ Hypermedia as the Engine of Application State Getting to JSON:API - Matt Stauffer
  6. REST misconceptions: ▸ REST has no preferred URL patterns ▸

    REST needn't be JSON ▸ Most REST-ish APIs aren't actually RESTful, but that's fine Getting to JSON:API - Matt Stauffer
  7. AN EXAMPLE OF EMBEDDED HYPERMEDIA /* GET /departments/10 */ {

    "type": "departments", "id": "10", "attributes": { "name": "Information Technology" } } Getting to JSON:API - Matt Stauffer
  8. AN EXAMPLE OF EMBEDDED HYPERMEDIA /* GET /departments/10 */ {

    "type": "departments", "id": "10", "attributes": { "name": "Information Technology" }, "links": [ { "href": "departments/10/employees", "rel": "employees", "type" : "GET" } ] } Getting to JSON:API - Matt Stauffer
  9. "Hard coding URIs in your documentation and thus also in

    all of the clients of your API, is a clear violation of the REST constraint that hypermedia must be the engine of application state." - Asbjørn Ulsberg Getting to JSON:API - Matt Stauffer
  10. WE'RE IN THE BAD OLD DAYS WHEN IT COMES TO

    API STANDARDIZATION Getting to JSON:API - Matt Stauffer
  11. “Every sufficiently advanced API contains an ad hoc, informally- specified

    implementation of some media type on top of JSON.” - Steve Klabnik Getting to JSON:API - Matt Stauffer
  12. YOU MAY BE FAMILIAR... { "data": { "type": "articles", "id":

    "1", "attributes": { // ... this article's attributes }, "relationships": { // ... this article's relationships } } } Getting to JSON:API - Matt Stauffer
  13. BUT WHAT ABOUT... ▸ Compound documents in flat included key

    ▸ Content negotiation requirements ▸ Custom error syntax ▸ Nested include directives ▸ Sparse fieldsets ▸ Create/update/delete validation ▸ Relationship modification endpoints Getting to JSON:API - Matt Stauffer
  14. THE BIG PIECES OF JSON:API ▸ Document structure ▸ HTTP

    standards Getting to JSON:API - Matt Stauffer
  15. DOCUMENT STRUCTURE IN JSON:API ▸ data ▸ included ▸ links

    ▸ errors ▸ meta ▸ jsonapi Getting to JSON:API - Matt Stauffer
  16. DOCUMENT STRUCTURE IN JSON:API ▸ data ▸ included ▸ links

    ▸ errors ▸ meta ▸ jsonapi Getting to JSON:API - Matt Stauffer
  17. { "jsonapi": { "version": "1.0" }, "meta": { "i can

    do anything": { "i want": ["in", "here"] } } } Getting to JSON:API - Matt Stauffer
  18. DOCUMENT STRUCTURE IN JSON:API ▸ data ▸ included ▸ links

    ▸ errors ▸ meta ▸ jsonapi Getting to JSON:API - Matt Stauffer
  19. { "data": [{ "type": "articles", "id": "1" },{ "type": "articles",

    "id": "42" }] } Getting to JSON:API - Matt Stauffer
  20. Optional sections { "type": "articles", "id": "1", "attributes": { //

    key/value pairs of all attributes }, "relationships": { // relationship objects }, "links": { // link objects }, "meta": { // key/value pairs } } Getting to JSON:API - Matt Stauffer
  21. Attributes { "type": "articles", "id": "1", "attributes": { "title": "The

    greatest post ever", "body": "Lorem ipsum" }, } Getting to JSON:API - Matt Stauffer
  22. Relationships { "type": "articles", "id": "1", "relationships": { "author": {

    "data": { "type": "people", "id": "5" } } } } Getting to JSON:API - Matt Stauffer
  23. DOCUMENT STRUCTURE IN JSON:API ▸ data ▸ included ▸ links

    ▸ errors ▸ meta ▸ jsonapi Getting to JSON:API - Matt Stauffer
  24. COMPOUND DOCUMENTS { "data": { "type": "articles", "id": "123", "attributes":

    {}, "relationships": { "author": { "data": { "type": "people", "id": "5" }} } }, "included": [{ "type": "people", "id": "5", "attributes": { "name": "Phil the GOAT" } }] } Getting to JSON:API - Matt Stauffer
  25. DOCUMENT STRUCTURE IN JSON:API ▸ data ▸ included ▸ links

    ▸ errors ▸ meta ▸ jsonapi Getting to JSON:API - Matt Stauffer
  26. LINKS { "data": [{}, {}, {}], "links": { "self": "http://site.com/articles?page=3",

    "first": "http://site.com/articles?page=1", "prev": "http://site.com/articles?page=2", "next": "http://site.com/articles?page=4", "last": "http://site.com/articles?page=42" } } Getting to JSON:API - Matt Stauffer
  27. DOCUMENT STRUCTURE IN JSON:API ▸ data ▸ included ▸ links

    ▸ errors ▸ meta ▸ jsonapi Getting to JSON:API - Matt Stauffer
  28. ERRORS { "errors": [{ "id": "SERVER500C1", "links": {}, "status": "500",

    "code": "SERVER500C1", "title": "A server error has occurred.", "detail": "Longer message here.", "source": { /* Object containing references to the source */}, "meta": {} }] } Getting to JSON:API - Matt Stauffer
  29. ERRORS { "errors": [{ "status": "500", "title": "A server error

    has occurred." }] } Getting to JSON:API - Matt Stauffer
  30. DOCUMENT STRUCTURE IN JSON:API ▸ data ▸ included ▸ links

    ▸ errors ▸ meta ▸ jsonapi Getting to JSON:API - Matt Stauffer
  31. HTTP SPECS IN JSON:API ▸ Content Negotiation ▸ Verbs &

    Status Codes ▸ Query parameters (include, page, sort, filter, fields) Getting to JSON:API - Matt Stauffer
  32. VERBS & STATUS CODES Use them. The way they were

    meant to be used. Note: If a JSON:API feature is requested and you don't support it (e.g. ? include), return a 400 BAD REQUEST Getting to JSON:API - Matt Stauffer
  33. Important note: You don't have to fully adhere to a

    standard to learn from it. Getting to JSON:API - Matt Stauffer
  34. JSON:API: "A specification for Fetching and Mutating a Graph of

    Data" — Dan Gebhardt Getting to JSON:API - Matt Stauffer
  35. class ArticleResource { public function toArray() { return [ 'data'

    => [ 'type' => 'articles', 'id' => $this->resource->id, ], 'attributes' => [ 'title' => $this->resource->title, ], ]; } } Getting to JSON:API - Matt Stauffer
  36. class ArticleController { public function index() { $articles = QueryBuilder::for(Article::class)

    ->allowedIncludes(['author', 'comments']) ->allowedSorts(['created_at', 'title']) ->paginate(); return new ArticleCollection($articles); } } Getting to JSON:API - Matt Stauffer
  37. CONCLUSION Good for simple applications, but lots of work for

    full implementation. With Spatie Query Builder, you can get far. But no flat included. Getting to JSON:API - Matt Stauffer
  38. public function index() { $articles = QueryBuilder::for(Article::class) ->allowedIncludes(['author', 'comments']) ->allowedSorts(['created_at',

    'title']) ->paginate(5); return fractal($articles, new ArticleTransformer) ->withResourceName('articles') ->respondJsonApi(); } Getting to JSON:API - Matt Stauffer
  39. class AuthorTransformer extends TransformerAbstract { protected $defaultIncludes = []; protected

    $availableIncludes = ['articles']; public function transform(User $user) { return [ 'id' => (int) $user->id, 'name' => $user->name, ]; } public function includeArticles(User $user) { return $this->collection($user->articles, new ArticleTransformer, 'articles'); } } Getting to JSON:API - Matt Stauffer
  40. CONCLUSION Great for serializing even complex use cases, and great

    if you're a Fractal fan, but there's still manual work to be done. Also requires Spatie Query Builder to get far. Includes flat included for free. Getting to JSON:API - Matt Stauffer
  41. class Schema extends SchemaProvider { protected $resourceType = 'articles'; public

    function getId($resource) { return (string) $resource->getRouteKey(); } public function getAttributes($resource) { return [ 'title' => $resource->title, 'body' => $resource->body, ]; } public function getRelationships($resource, $isPrimary, array $includedRelationships) { return [ 'author' => [ self::SHOW_SELF => true, self::SHOW_RELATED => true, self::SHOW_DATA => isset($includedRelationships['author']), self::DATA => function () use ($resource) { return $resource->author; }, Getting to JSON:API - Matt Stauffer
  42. class Adapter extends AbstractAdapter { protected $attributes = []; protected

    $filterScopes = []; protected function author() { return $this->belongsTo('user'); } protected function comments() { return $this->hasMany(); } } Getting to JSON:API - Matt Stauffer
  43. CONCLUSION Steep learning curve, but the best way to get

    even close to full JSON:API. Getting to JSON:API - Matt Stauffer
  44. CACHING ▸ Cache DB requests ▸ Full HTTP request caching

    ▸ Intermediary caching (Varnish, Cloudflare) Getting to JSON:API - Matt Stauffer
  45. ▸ Relationship endpoints ▸ True HATEOAS ▸ Etags & cache-control/expires

    ▸ JSON Schema for defining types (client-side validation!) ▸ HTTP/2 Server Push ▸ JSON:API 1.1 Getting to JSON:API - Matt Stauffer
  46. TL;DR ▸ API standards are great ▸ JSON:API is a

    great standard ▸ Gradual adoption means there are no judgments! ▸ If you don't adopt something, error if it's asked for ▸ Eloquent Resources/Fractal + Spatie Query Builder for JSON:API lite, Laravel-JSON-API for full spec Getting to JSON:API - Matt Stauffer