Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JSON API: Možná nepotřebujete GraphQL

104 views

Published on

phpCE, 27. října 2018, Praha

Asi jste v poslední době slyšeli o GraphQL. Je tedy opravdu čas opustit REST API, jak někteří tvrdí? Myslím, že zdaleka ne - a chtěl bych vám ukázat existující alternativu pro vytváření dobrých REST API - standard JSON API, okolo kterého se rozvíjí zajímavý ekosystém i v PHP.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

JSON API: Možná nepotřebujete GraphQL

  1. 1. … možná nepotřebujete GraphQL phpCE, 27. října 2018 Ondřej Machulda  @OndraM Anotovaná verze slajdů
  2. 2. GraphQL API? Zdroj: https://twitter.com/samerbuna/status/644548922979954688 Ovládne GraphQL svět? Je REST API mrtvé? Už tři roky?
  3. 3. GraphQL Zdroj: Jeremykemp at English Wikipedia, https://commons.wikimedia.org/wiki/File:Gartner_Hype_Cycle.svg Spíše jde o klasický hype cyklus, kterým si projde většina technologií. Prošel si jím i konec konců REST.
  4. 4. GraphQL Zdroj: https://twitter.com/tomdale/status/786954892342681600 GraphQL ale není náhrada REST. Není to REST 2.0. Je to (zjednodušeně řečeno) další paradigma pro API.
  5. 5. RPC? REST? GraphQL? Alternativ je přitom více. Narozdíl od RPC a RESTu, což jsou obecné koncepty, je GraphQL konkrétní specifická implementace API. Ale to taky znamená, že je vhodné pro specifické použití.
  6. 6. Zdroj: Phil Sturgeon, https://philsturgeon.uk/2018/05/21/picking-an-api-paradigm-implementation/ Přednáška o různých API paradigmatech: Nate Barbettini – API Throwdown: RPC vs REST vs GraphQL https://www.youtube.com/watch?v=IvsANO0qZEg Phil Sturgeon – Picking the right API Paradigm philsturgeon.uk/2018/05/21/picking-an-api-paradigm-implementation/ Je třeba znát tradeoffy různých typů API. Navíc každý projekt má jiné potřeby a požadavky. Ale pro značnou situací, které jako PHP vývojáři řešíme, pro nás bude výhodnější REST API.
  7. 7. Zdroj: Ali Emirov, https://www.flickr.com/photos/27435717@N00/44545597592/, CC BY-NC 2.0Zdroj: https://www.pexels.com/photo/blur-breakfast-chef-cooking-262978/ REST GraphQLaurace Phil Sturgeon: A No Nonsense GraphQL and REST Comparison: https://www.youtube.com/watch?v=vgm_uGmspMI Zdeněk Němec: REST vs. GraphQL: A Critical Review https://blog.goodapi.co/rest-vs-graphql-a-critical-review-5f77392658e7 REST je jako objednat si v RESTauraci. GraphQL je jako sestavit si jídlo ze švédského stolu.
  8. 8. REST je obecný koncept REST je soubor obecných myšlenek – mohou jej naplňovat různé podoby API. To může být problém, protže spoustu rozhodnutí musí udělat ten, kdo to implementuje. A tak může spoustu věci udělat špatně. Je třeba rozumět konceptům REST a HTTP. A pokud nechci používat koncepty RESTu – třeba hypermedia, tak stejně nemůžete postavit REST API.
  9. 9. Vlastnosti REST API
  10. 10. Orientovaný na resource Unikátní URL pro každou resource: – /articles/c4b12316-cb13-11e8-a8d5-f2801f1b9f/ – /articles/1337/ – /articles/1337/comments/3383b04c/ Resource = podstatné jméno
  11. 11. Využívá HTTP protokol HTTP metody = pojmenování operací nad resources GET = načíst resource, nic se nemění – GET https://blog/articles/1337/ POST = vytvořit novou resource (zpravidla nevíme její ID) – POST https://blog/articles/ PUT = update (přepsání) celé resource – PUT https://blog/articles/1337/ DELETE = mazání – DELETE https://blog/articles/1337/ PATCH = částečný update – PATCH https://blog/articles/1337/ Ale nejen HTTP metody – REST využívá i další části standardní HTTP infrastruktury: cachování, hlavičky...
  12. 12. HATEOAS Hypermedia as the Engine of Application State Čím na webu hyperlinky, tím v API hypermedia
  13. 13. HATEOAS Hypermedia as the Engine of Application State { "articleId": 1337, "title": "Article Title", "text": "Lorem ipsum", "links": [ { "href": "/1337/comments", "rel": "comments", "type": "GET" }, { "href": "/1337/tags", "rel": "tags", "type": "GET" } ] }
  14. 14. https://jsonapi.org/
  15. 15. JSON API 1.0 (2015-05-29) Konvence, jak správně řešit různé faktory REST API Předchází znovuvynalézání kola JSON API je zhmotnění řady best practices pro tvorbu REST API. Popisuje, jak řešit plno věcí, které můžeme v API potřebovat.
  16. 16. GET /api/articles Accept: application/vnd.api+json --- HTTP/1.1 200 OK Content-Type: application/vnd.api+json { … } Request / response
  17. 17. GET /api/articles/1337 { "data": { "type": "articles", "id": "1337", "attributes": { "title": "Article title", "datePublished": "2018-10-27T13:33:30", } } } Single resource object
  18. 18. Kolekce resource objectů GET /api/articles { "data": [ { "type": "articles", "id": "1337", "attributes": { "title": "Article title" } }, { "type": "articles", "id": "1338", "attributes": { "title": "Another article title" } } ] }
  19. 19. Vytváření resources POST /api/articles Accept: application/vnd.api+json Content-Type: application/vnd.api+json { "data": { "type": "articles", "attributes": { "title": "New article" } } }
  20. 20. Vytváření resources HTTP/1.1 201 Created Content-Type: application/vnd.api+json Location: https://blog/articles/1339 { "data": { "type": "articles", "id": "1339", "attributes": { "title": "New article" } } } Další CRUD operace, HTTP kódy aj. viz jsonapi.org/format/#crud
  21. 21. Hypermedia GET /api/articles/1337 { "data": { "type": "articles", "id": "1337", "attributes": { "title": "Article title" }, "relationships": { "author": { "links": { "self": "https://blog/articles/1337/relationships/author", "related": "https://blog/articles/1337/author" } }, "comments": { "links": { "self": "https://blog/articles/1/relationships/comments", "related": "https://blog/articles/1/comments" } } } } }
  22. 22. Chyby { "errors": [ { // všechna pole jsou volitelná 👌 "id": "37d5e868-ad31", // Unikátní ID této chyby "status": "422", // HTTP status kód "code": "API-13B", // Vlastní chybový kód // Lidsky čitelný popis, který se pro stejnou chybu nemění: "title": "Invalid value", // Lidský detail chyby, může být lokalizovaný (jako title): "detail": "First name must contain at least three characters.", // zdroj chyby jako JSON pointer (RFC 6901) "source": { "pointer": "/data/attributes/firstName" }, // Cokoliv, třeba stacktrace "meta": { "file": "src/Controller/ArticleController.php"} } ] }
  23. 23. Složené (compound) dokumenty GET /api/articles/1337 { "data": { "type": "articles", "id": "1337", "relationships": { "author": { "links": { "self": "/articles/1337/relationships/author", "related": "/articles/1337/author" }, "data": { "type": "people", "id": "333" } } } }, } "included": [ { "type": "people", "id": "333", "attributes": { "firstName": "John", "lastName": "Doe" }, "links": { "self": "/people/333" } } ] Resource z relationship můžeme najít v cache či v identity mapě (podle jejího type a id). Když ji nemáme, můžeme si ji načíst z jejího linku. Ale když bychom potřebovali ušetřit requesty (N+1 problém), může nám pomoci tzv. sideloading, kdy odpověď ten dokument rovnou obsahuje („compound“ dokument).
  24. 24. Stránkování Filtrování ¯_( ツ )_/¯ – jen rezervovaný query parametr filter, nic víc Řazení Sparse fileds – definice položek, které chci vrátit – GET /articles?fields[articles]=title,body Co dále umí JSON API řešit Možnost říct si, jaké položky je přece unikátní vlastnost GraphQL! Oh wait, v RESTu to jde taky. 🤔
  25. 25. Nástroje pro JSON API v PHP
  26. 26. github.com/json-api-php/json-api $document = new DataDocument( new ResourceObject( 'articles', // type '1337', // id // ... a jaké další chceme položky: new Attribute('title', 'Article Title'), new ToOne( // relationship 'author', new ResourceIdentifier('author', '333'), new SelfLink('/articles/1337/relationships/author'), new RelatedLink('/articles/1337/author') ) ) ); echo json_encode($document); ● Jenom imutabilní datové entity JSON API formátu ● To nejsnazší, co může každý u nového API udělat
  27. 27. github.com/json-api-php/json-api { "data": { "type": "articles", "id": "1337", "attributes": { "title": "Article Title" }, "relationships": { "author": { "data": { "type": "author", "id": "333" }, "links": { "self": "/articles/1337/relationships/author", "related": "/articles/1337/author" } } } } }
  28. 28. github.com/json-api-php/json-api $errorDocument = new ErrorDocument( new Error( new Status('404'), new Code('not_found'), new Title('Resource not found'), new Detail('We tried hard but could not find it 😞'), ) ); echo json_encode($errorDocument); { "errors": [ { "status": "404", "code": "not_found", "title": "Resource not found", "detail": "We tried hard but could not find it " } ] }
  29. 29. # config/routes.yaml json_api: resource: "@EnmJsonApiServerBundle/Resources/config/routing.xml" GET /{type} GET /{type}/{id} GET /{type}/{id}/relationships/{relationship} GET /{type}/{id}/{relationship} POST /{type} PATCH /{type}/{id} DELETE /{type}/{id} POST /{type}/{id}/relationships/{relationship} PATCH /{type}/{id}/relationships/{relationship} DELETE /{type}/{id}/relationships/{relationship} eosnewmedia/json-api-server-bundle ● Bundle pro Symfony ● Request/response, exception listener...
  30. 30. # config/services.yaml AppRequestHandlerArticlesRequestHandler: tags: - { name: json_api_server.request_handler, type: 'articles' } eosnewmedia/json-api-server-bundle
  31. 31. // src/RequestHandler/ArticlesRequestHandler.php class ArticlesRequestHandler implements RequestHandlerInterface { use NoRelationshipFetchTrait; use NoRelationshipModificationTrait; use NoResourceDeletionTrait; public function fetchResource(RequestInterface $request): ResponseInterface { $resource = new JsonResource( 'articles', // type $request->id(), // id $this->repository->find($request->id())->toArray() // attributes ); $document = new Document($resource); return new DocumentResponse($document, [/* extra headers */], 200); } public function fetchResources(RequestInterface $request): ResponseInterface { /* ... */ } public function createResource(RequestInterface $request): ResponseInterface { /* ... */ } public function patchResource(RequestInterface $request): ResponseInterface { /* ... */ } } eosnewmedia/json-api-server-bundle
  32. 32. // src/RequestHandler/ArticlesRequestHandler.php class ArticlesRequestHandler implements RequestHandlerInterface { use NoRelationshipFetchTrait; use NoRelationshipModificationTrait; use NoResourceDeletionTrait; public function fetchResource(RequestInterface $request): ResponseInterface { $resource = new JsonResource( 'articles', // type $request->id(), // id $this->repository->find($request->id())->toArray() // attributes ); $document = new Document($resource); return new DocumentResponse($document, [/* extra headers */], 200); } public function fetchResources(RequestInterface $request): ResponseInterface { /* ... */ } public function createResource(RequestInterface $request): ResponseInterface { /* ... */ } public function patchResource(RequestInterface $request): ResponseInterface { /* ... */ } } eosnewmedia/json-api-server-bundle GET /{type}/{id} GET /{type} POST /{type} PATCH /{type}/{id} Traity na endpointy, které nepotřebujeme.
  33. 33. JSON API Playground https://jsonapiplayground.reyesoft.com/ JSON API Implementations https://jsonapi.org/implementations/ Další nástroje
  34. 34. Specifikace http://jsonapi.org/ Phil Sturgeon – blog https://blog.apisyouwonthate.com/ – knížka "Build APIs You Won't Hate" Blogpost + talk "The Benefits of Using JSON API" – https://nordicapis.com/the-benefits-of-using-json-api/ OpenAPI přednáška zítra, 10:00 – Boyan Yordanov: Beyond Documentation With OpenAPI Další zdroje
  35. 35. REST API neumírá, ale má nadále své místo GraphQL je alternativní typ API, který má své specifické použití
  36. 36. Pro vaše další REST API zvažte použití JSON API V PHP je na to plno nástrojů o/
  37. 37. joind.in/talk/9d53c 🔝 feedback 🙏  Ondřej Machulda  ondrejmachulda.cz  @OndraM

×