Slide 1

Slide 1 text

VarnaConf 2017 REST MORE WITH JSON:API AND FRACTAL Use open standards to build better and more maintainable APIs 1

Slide 2

Slide 2 text

$> WHOAMI $> BOYAN Developer at Shtrak Community cheerleader Newbie speaker and writer Organizer of PHP Varna 2 . 1

Slide 3

Slide 3 text

WHERE TO FIND ME @specter_bg boyanyordanov boyanyordanov 2 . 2

Slide 4

Slide 4 text

WHAT IS REST ? REpresentational State Transfer 3 . 1

Slide 5

Slide 5 text

WHAT IS REST ? pretty URLs ? HTTP verbs for actions ? None of those ? 3 . 2

Slide 6

Slide 6 text

ACHIEVING ETERNAL GLORY https://martinfowler.com/articles/richardsonMaturityMo 3 . 3

Slide 7

Slide 7 text

HOW OFTEN DO YOU SEE POST /article/1/delete POST /article/1/publish GET /get-published-articles 3 . 4

Slide 8

Slide 8 text

OR EVEN WORSE GET /REST/article/create?title="No REST from bad design" 3 . 5

Slide 9

Slide 9 text

SO ARE WE ... ? ... probably NOT. But does it really matter? 3 . 6

Slide 10

Slide 10 text

OPEN STANDARDS What makes an open standard ? Cooperation Adherence to Principles Due process, Broad concensus, Transparency, Balance, Openness Collective Empowerment Availability Voluntary Adoption 4 . 1

Slide 11

Slide 11 text

EXAMPLES WE USE EVERYDAY HTTP CSS, HTML, ECMAScript C, C++, Java ( ), C# PHP RFC process and PHP-Fig PSRs (sort of) JCP 4 . 2

Slide 12

Slide 12 text

OPEN STANDARDS AND APIS Auth and security Oauth 1/2, OpenID Connect, JWT Documentation / helper openapi/swagger, api blueprint, RAML, json- schema Representation / content types HAL, Problem JSON, Hydra and json-ld, JSON-API Other GraphQL 5

Slide 13

Slide 13 text

BENEFITS OF USING STANDARDS FOR APIS easier to design and build easier to maintain easier to consume easier to test 6 . 1

Slide 14

Slide 14 text

6 . 2

Slide 15

Slide 15 text

7

Slide 16

Slide 16 text

OVERVIEW well-de ned conventions for listing, creating, updating and deleting resources recommendations for details not covered in the main spec server and client libraries in most popular languages 8

Slide 17

Slide 17 text

http://www.commitstrip.com/en/2017/02/20/no-ones- fault/ 9

Slide 18

Slide 18 text

{ "data": { "type": "articles", "id": "1", "attributes": { "title": "JSON API paints my bikeshed!" }, "relationships": { "author": { "links": {"self": "...", "related": "..."}, "data": { "type": "people", "id": "9" } } }, "links": { "self": "/articles/1"} } } 10

Slide 19

Slide 19 text

THE MEDIA TYPE Client call Server response Accept: application/vnd.api+json Content-Type: application/vnd.api+json Content-type: application/vnd.api+json 11 . 1

Slide 20

Slide 20 text

DECONSTRUCTING THE COMPOUND DOCUMENT TOP LEVEL ATTRIBUTES data errors meta links jsonapi included 11 . 2

Slide 21

Slide 21 text

TYPES OF OBJECTS resource object link object resource identi er object error object 12 . 1

Slide 22

Slide 22 text

THE RESOURCE OBJECT { "type": "meetups", "id": "1", "attributes": { "name": "PHP Varna", "description": "The php UG of Varna", "next-event": "2017-05-18T19:30:00.200Z" } } 12 . 2

Slide 23

Slide 23 text

LINKS OBJECT "links": { "self": "/meetups/1" }, "data": { ... } 12 . 3

Slide 24

Slide 24 text

"data": { ... }, "relationships": { "links": { "events": { "related": "/meetups/1/events", "meta": { "count": 50 } } } } 12 . 4

Slide 25

Slide 25 text

RESOURCE IDENTIFIER OBJECT { "id": "1", "type": "events", "meta": { "attendee-count": 50 } } 12 . 5

Slide 26

Slide 26 text

RELATIONSHIPS OBJECT "relationships": { "location": { "links": { "self": "events/1/relationships/venue", "related": "events/1/venue" }, "data": { "type": "venues", "id": "1" } } } 12 . 6

Slide 27

Slide 27 text

PUTTING IT ALL TOGETHER GET /meetups HTTP/1.1 Accept: application/vnd.api+json { "data": [{ "type": "meetups", "id": "1", "attributes": { "name": "PHP Varna", "description": "The php UG of Varna", "next-event": "2017-05-18T19:30:00.200Z" }, "relationships": { "organizer": { "data": { "type": "users", "id": "1" } } } }] } 13

Slide 28

Slide 28 text

RELATIONSHIPS Change the meetup place PATCH /events/123/relationships/venue HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data": { "type": "venues", "id": "12" } } 14 . 1

Slide 29

Slide 29 text

Remove the venue (untill we nd a new one?) PATCH /events/123/relationships/venue HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data": null } 14 . 2

Slide 30

Slide 30 text

SIDELOADING RELATIONSHIPS (GET MEETUP AND VENUE) GET /events/1?include=venue HTTP/1.1 HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "data": { // resource object for the event }, "included": [ { "type": "venues", "id": "1", "attributes": { "name": "VarnaLab", "address": "ul. 'Pencho Slaveykov' 50" } } ] 14 . 3

Slide 31

Slide 31 text

ERROR OBJECTS HTTP/1.1 422 Unprocessable Entity Content-Type: application/vnd.api+json { "errors": [ { "status": "422", "source": { "pointer": "/data/attributes/name" }, "title": "Invalid Attribute", "detail": "Meetup name must contain at least three characters." } ] } 15

Slide 32

Slide 32 text

QUERY PARAMS ltering sorting GET /meetups?filter[location]="Varna" HTTP/1.1 Accept: application/vnd.api+json GET /meetups?sort=name HTTP/1.1 Accept: application/vnd.api+json GET /meetups?sort=-name HTTP/1.1 Accept: application/vnd.api+json 16 . 1

Slide 33

Slide 33 text

sparse eldsets GET /meetups?fields[meetups]=name HTTP/1.1 Accept: application/vnd.api+json { "data": [ { "id": "1", "type": "meetups", "attributes": { "name": "PHP Varna" } }, { "id": "2", "type": "meetups", "attributes": { "name": "WordPress Varna" } } 16 . 2

Slide 34

Slide 34 text

COMPLEX QUERY GET /events?filter[meetup]="PHP Varna" &filter[venue]="VarnaLab" &filter[date_after]="26-08-2017" &include=venues &fields[events]=name,date &fields[venues]=address HTTP/1.1 Accept: application/vnd.api+json 16 . 3

Slide 35

Slide 35 text

PAGINATION GET /meetups?page[number]=3&page[size]=1 HTTP/1.1 Accept: application/vnd.api+json 16 . 4

Slide 36

Slide 36 text

{ "meta": { "total-pages": 12 }, "data": [ { "type": "meetups", "id": "2", "attributes": { "name": "WordPress Varna", "description": "The place for WordPress entusiasts", "next-meetup": "", } } ], "links": { "self": "/meetups?page[number]=2&page[size]=1", "first": "/meetups?page[number]=1&page[size]=1", "prev": "/meetups?page[number]=2&page[size]=1", "next": "/meetups?page[number]=4&page[size]=1", "last": "/meetups?page[number]=12&page[size]=1" } } 16 . 5

Slide 37

Slide 37 text

RESOURCE OPERATIONS listing get individual resource create update delete 17

Slide 38

Slide 38 text

CREATE A MEETUP POST /meetups HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data": { "type": "meetups", "attributes": { "name": "JS Varna?", "desription": "Does Varna need another meetup?" }, "relationships": {//optional} } } 18 . 1

Slide 39

Slide 39 text

RESPONSE HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "data": { "type": "meetups", "id": 3, "attributes": { "name": "JS Varna?", "desription": "Does Varna need another meetup?" } } } 18 . 2

Slide 40

Slide 40 text

UPDATING A MEETUP PATCH /meetups/3 HTTP/1.1 Content-Type: application/vnd.api+json Accept: application/vnd.api+json { "data": { "type": "meetups", "attributes": { "name": "JS Varna", "desription": "Happening soon in a hackerspace near you" } } } 18 . 3

Slide 41

Slide 41 text

CANCELING AN EVENT RESPONSE DELETE /events/123 Content-Type: application/vnd.api+json Accept: application/vnd.api+json HTTP/1.1 204 No Content Content-Type: application/vnd.api+json 18 . 4

Slide 42

Slide 42 text

A 404 ERROR RESPONSE GET /meetups/1234 Content-Type: application/vnd.api+json Accept: application/vnd.api+json HTTP/1.1 404 Not Found Content-Type: application/vnd.api+json { "errors": [ { "title": "Resource Not Found", "content": "There is no event with id 1234" } ] } 18 . 5

Slide 43

Slide 43 text

FRACTAL Like a view layer for your JSON/YAML/etc. 19

Slide 44

Slide 44 text

MAIN CONCEPTS Fractal Manager Resource Includes Serializer Transformer Pagination via paginator via cursor 20

Slide 45

Slide 45 text

RESOURCES & TRANSFORMERS Closure transformer use League\Fractal\Resource\Item; $meetup = retrieveFromDB(); $meetupResource = new Item($meetup, function($m) { return [ 'id' => $meetup->id, 'name' => $meetup->name, 'description' => $meetup->description, 'next-meetup' => $meetup->events->getNext()->date ]; }); 21 . 1

Slide 46

Slide 46 text

Class transformer use League\Fractal\TransformerAbstract; use League\Fractal\Resource\Collection; class MeetupTransformer extends TransformerAbstract { public function transform(Meetup $meetup) { return [ 'id' => $meetup->id, 'name' => $meetup->name, 'description' => $meetup->description, 'next-meetup' => $meetup->events->getNext()->date ]; } } $data = new Collection($meetups, new MeetupTransformer); 21 . 2

Slide 47

Slide 47 text

De ning includes use League\Fractal\TransformerAbstract; use League\Fractal\Resource\Collection; class MeetupTransformer extends TransformerAbstract { protected $availableIncludes = ['venues']; public function transform($meetup) { ... } public function includeVenue($meetup) { $venue = $meetup->venue; $transformer = new VenueTransformer; return $this->item($venue, $transformer, 'venues'); } } $data = new Collection($meetups, new MeetupTransformer); 21 . 3

Slide 48

Slide 48 text

Creating a resource use League\Fractal\Serializer\JsonApiSerializer; $manager = new League\Fractal\Manager(); $baseUrl = 'http://meetup.dev'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); // ORM call $meetup = Meetup::find(1); // Make a resource out of the data and $resource = new Item($meetup, new MeetupTransformer, 'meetups'); // Run all transformers $manager->createData($resource)->toArray(); 21 . 4

Slide 49

Slide 49 text

Result [ "data" => [ "type" => "meetups", "id" => "1", "attributes" => [ "name" => "PHP Varna", "description" => "The php UG in Varna", "next-meetup" => "2017-05-18T19:30:00.200Z" ] ], "links" => [ "self" => "http://meetup.dev/meetups/1" ] ] 21 . 5

Slide 50

Slide 50 text

Using includes use League\Fractal\Serializer\JsonApiSerializer; $manager = new League\Fractal\Manager(); $baseUrl = 'http://meetup.dev'; $manager->setSerializer(new JsonApiSerializer($baseUrl)); // ORM call $meetup = Meetup::find(1); if (isset($_GET['include'])) { $fractal->parseIncludes($_GET['include']); } 21 . 6

Slide 51

Slide 51 text

WHAT IS MISSING FROM THE SPEC? ltering sorting sparse eldsets pagination - present and usable but not spec compliant 22 . 1

Slide 52

Slide 52 text

PAGINATION use League\Fractal\Resource\Collection; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use Acme\Model\Book; use Acme\Transformer\BookTransformer; $paginator = Meetup::paginate(); $meetups = $paginator->getCollection(); $resource = new Collection($meetups, new MeetupTransformer); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); 22 . 2

Slide 53

Slide 53 text

SUPPORTED ADAPTERS Laravel Doctrine Zend Framework Phalcon Pagerfanta your own PaginatorInterface implementation 22 . 3

Slide 54

Slide 54 text

WHAT IS NOT INCLUDED ON PURPOSE? content negotiation response code handling error objects 22 . 4

Slide 55

Slide 55 text

HANDLING ERROR OBJECTS 23

Slide 56

Slide 56 text

Use a base class class JsonApiError extends \Exception { protected $status; protected $title; protected $detail; protected $source; protected $meta; public function toArray() {...} } 24 . 1

Slide 57

Slide 57 text

Extend it for speci c errors class NotFoundError extends JsonApiError { protected $status = 404; protected $title = 'Resource not found'; } 24 . 2

Slide 58

Slide 58 text

EASIER FRACTAL composer install spatie/fractalistic A developer friendly wrapper around Fractal 25 . 1

Slide 59

Slide 59 text

Fractal::create() ->collection($meetups) ->transformWith(new MeetupTransformer()) ->includeVenue() ->toArray(); 25 . 2

Slide 60

Slide 60 text

ALTERNATIVES neomerx/json-api resource transformation query parameters handling errors 26

Slide 61

Slide 61 text

RESOURCES http://jsonapi.org/ http://fractal.thephpleague.com/ https://www.ics.uci.edu/~ elding/pubs/dissertation/ e Steve Klabnik's talk: past, present and future of json-ap https://youtu.be/Foi54om6oGQ discussion: who's using json-api - https://github.com/jso api/issues/825 27

Slide 62

Slide 62 text

THANK YOU 28

Slide 63

Slide 63 text

QUESTIONS ? 29