Slide 1

Slide 1 text

PRAGMATIC APIS 101 William Durand — October 29th, 2016 — Symfony Camp UA 1

Slide 2

Slide 2 text

WHAT'S REST? 2 . 1

Slide 3

Slide 3 text

REPRESENTATIONAL STATE TRANSFER REST is the underlying architectural principle of the web. It is formalized as a set of constraints, described in Roy Fielding's PhD dissertation. 2 . 2

Slide 4

Slide 4 text

RICHARDSON MATURITY MODEL 2 . 3

Slide 5

Slide 5 text

LEVELS 0, 1, AND 2 It is all about resources Client uses specific HTTP verbs Server uses HTTP status codes Content Negotiation 2 . 4

Slide 6

Slide 6 text

LEVEL 3 - HYPERMEDIA CONTROLS Service discovery via relations Hypermedia formats (e.g. HTML, HAL, JSONAPI) Resources are not important, their representations are! URI design does not matter (URI template) Client's experience is crucial! Protocol & application semantics 2 . 5

Slide 7

Slide 7 text

HATEOAS Hypermedia As The Engine Of Application State It means that hypermedia should be used to find your way through the API. It is all about state transitions. Your application is just a big state machine. 2 . 6

Slide 8

Slide 8 text

RESOURCE VS REPRESENTATION A resource can be anything, and can have more than one representation. A representation describes resource state. 2 . 7

Slide 9

Slide 9 text

PROTOCOL SEMANTICS How the underlying resource should behave under HTTP? 2 . 8

Slide 10

Slide 10 text

APPLICATION SEMANTICS What, specifically, will happen to the application or resource state? 2 . 9

Slide 11

Slide 11 text

THE SEMANTIC CHALLENGE* Some standards have a good protocol-level semantics but no application-level semantics (Collection+JSON, Atom). Some standards define a lot of application-level semantics but no protocol semantics (Microformats, Microdata). * = as described in Richardson and Amundsen's “RESTful Web APIs” book 2 . 10

Slide 12

Slide 12 text

PROFILES A profile is defined to not alter the semantics of the resource representation itself, but to allow clients to learn about additional semantics, . Does not have to be machine-readable but recommended. RFC 6906 2 . 11

Slide 13

Slide 13 text

EXAMPLES Traditional API documentation XMDP (for XHTML documents) ALPS (XMDP on steroids) Embedded doc. (as in HAL/Siren) JSON-LD, JSON Schema 2 . 12

Slide 14

Slide 14 text

STATE OF THE ART INDUSTRY 3 . 1

Slide 15

Slide 15 text

WE ALL WANT RESTFUL APIS 3 . 2

Slide 16

Slide 16 text

YES. 3 . 3

Slide 17

Slide 17 text

IS MY API RESTFUL WHEN I USE JSON? , The REST CookBook. Short answer: no. Long answer: no, not yet. RESTful API must use hypermedia formats. JSON is not a hypermedia format 3 . 4

Slide 18

Slide 18 text

NO RELATIONS = NO REST , Roy Fielding. If the engine of application state (and hence the API) is not being driven by hypertext, then it cannot be RESTful and cannot be a REST API 3 . 5

Slide 19

Slide 19 text

S/REST/HTTP++/ REST APIs are a myth, i.e. too complex in real life. , Steve Klabnik. 99.99% of the RESTful APIs out there aren’t fully compliant with Roy Fielding’s conception of REST 3 . 6

Slide 20

Slide 20 text

ARE WE ALL SCREWED AND SHOULD WE ALL JUMP TO GRAPHQL? 3 . 7

Slide 21

Slide 21 text

NOPE. 3 . 8

Slide 22

Slide 22 text

THE PRAGMATIC WAY Well-designed, pragmatic, and future-proof APIs. 4

Slide 23

Slide 23 text

 DOCUMENTATION 5 . 1

Slide 24

Slide 24 text

API BLUEPRINT 5 . 2

Slide 25

Slide 25 text

APIARY 5 . 3

Slide 26

Slide 26 text

APIARY 5 . 4

Slide 27

Slide 27 text

AGLIO 5 . 5

Slide 28

Slide 28 text

MOOOOCKS! (Apiary / ) Drakov 5 . 6

Slide 29

Slide 29 text

JSON SCHEMA (Apiary / Aglio thanks to ) MSON + Attributes + data (object) + id: `123` (string, required) - The identifier { "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "properties": { "data": { "type": "object", "properties": { "id": { "type": "string", "description": "The identifier" } }, "required": [ "id" ] } } } 5 . 7

Slide 30

Slide 30 text

(ex-Swagger) 5 . 8

Slide 31

Slide 31 text

SYMFONY/PHP NelmioApiDocBundle ( ) by Liip (blog post) (JSON Schema) and (Open API) #900 A Tool to Convert NelmioApiDocBundle to Swagger PHP Jane Jane Open Api 5 . 9

Slide 32

Slide 32 text

ADVICES Write documentation first, then code Must be under version control Test your documentation (build / ) Dredd 5 . 10

Slide 33

Slide 33 text

 CODE 6 . 1

Slide 34

Slide 34 text

SYMFONY/PHP = Great for HTTP++ APIs + = HAL , , etc. Symfony REST Edition FOSRestBundle Hateoas Fractal Negotiation 6 . 2

Slide 35

Slide 35 text

6 . 3

Slide 36

Slide 36 text

6 . 4

Slide 37

Slide 37 text

JSON API application/vnd.api+json { "data": { "id": "41c6cf", "type": "sequence", "attributes": { "name": "> Demo 1", "sequence": "ATCGAATCGGTTTAAAATCGATTCCCGAAAA" } }, "links": { "parent": "/data/27d99b56", "self": "/data/27d99b56/41c6cf", "source": "https://example.org/demo-sequence.fasta", "profile": "/profiles/data/sequence" } } 6 . 5

Slide 38

Slide 38 text

ERRORS { "errors": [ { "code": "404", "status": "404", "title": "Not Found", "detail": "The URL you are trying to reach does not exist.", "links": { "about": "https://httpstatuses.com/404", "profile": "/profiles/errors" } } ] } 6 . 6

Slide 39

Slide 39 text

UPDATE WITH PUT PATCH (Yes... I wrote about ) PATCH /upload/jobs/27d99b56-9327-4d28-a69c-31229bf971aa Content-Type: application/vnd.api+json Accept: application/vnd.api+json // /!\ request payload below { "data": { "id": "27d99b56-9327-4d28-a69c-31229bf971aa", "type": "upload-jobs", "attributes": { "data_type": "sequence", "file_type": "fasta" } } } PATCH'ing correctly 6 . 7

Slide 40

Slide 40 text

SPARSE FIELDSETS  with the fields query parameter GET /data/7d99b56?fields[sequence]=name Accept: application/vnd.api+json "data": [ { "id": "41c6cf", "type": "sequence", "attributes": { "name": "> Demo 1" } }, { "id": "787ff2", "type": "sequence", "attributes": { "name": "> Seq. 2" } } ] 6 . 8

Slide 41

Slide 41 text

FILTERING with the filter query parameter GET /data/27d99b56?fields[sequence]=sequence \ &filter={"name": { "$regex": "/demo/", "$options": "i"}} Accept: application/vnd.api+json "data": [ { "id": "41c6cf", "type": "sequence", "attributes": { "sequence": "ATCGAATCGGTTTAAAATCGATTCCCGAAAA" } } ] 6 . 9

Slide 42

Slide 42 text

PAGINATION with the page query parameter { "data": [ ... ], "links": { "next": "/data/8f7d5ef1?page[cursor]=57de7ff7&page[limit]=1", "prev": "/data/8f7d5ef1?page[cursor]=-57de7ff7&page[limit]=1" }, "meta": { "page": { "after": "57de7ff7", "before": "-57de7ff7", "count": 3138, "limit": 1 } } } 6 . 10

Slide 43

Slide 43 text

INCLUSION with the include query parameter GET /data/27d99b56?include=ANYTHING Accept: application/vnd.api+json { "data": [ ... ], "included": [ ANYTHING ] } 6 . 11

Slide 44

Slide 44 text

ASYNCHRONOUS PROCESSING (1/3) POST /upload Content-Length: 87 Content-Type: text/csv Accept: application/vnd.api+json HTTP/1.1 202 Accepted Content-Type: application/vnd.api+json Content-Location: /upload/jobs/27d99b56 { "data": { "id": "27d99b56", "type": "upload-jobs", "attributes": { "status": "Processing..." } } } 6 . 12

Slide 45

Slide 45 text

ASYNCHRONOUS PROCESSING (2/3) GET /upload/jobs/27d99b56 Accept: application/vnd.api+json HTTP/1.1 303 See other Location: /data/27d99b56 6 . 13

Slide 46

Slide 46 text

ASYNCHRONOUS PROCESSING (3/3) Degraded mode for browsers with the Prefer header GET /upload/jobs/27d99b56 Accept: application/vnd.api+json Prefer: status=201 HTTP/1.1 201 Created Location: /data/27d99b56 6 . 14

Slide 47

Slide 47 text

RECAP' JSON API helps writing REST APIs It is OK to take shortcuts (sometimes) JSON schema for (application) semantics 6 . 15

Slide 48

Slide 48 text

 TESTING 7 . 1

Slide 49

Slide 49 text

PHPUNIT + GUZZLE Source: public function testPOST() { // ... create Guzzle $client $request = $client->post('/api/programmers', null, json_encode($data)); $response = $request->send(); $request = $client->post('/api/programmers', null, json_encode($data)); $response = $request->send(); $this->assertEquals(201, $response->getStatusCode()); $this->assertTrue($response->hasHeader('Location')); $data = json_decode($response->getBody(true), true); $this->assertArrayHasKey('nickname', $data); } https://knpuniversity.com/screencast/rest/testing-phpunit 7 . 2

Slide 50

Slide 50 text

BEHAT Source: # api/features/programmer.feature # ... Scenario: Create a programmer Given I have the payload: """ { "nickname": "ObjectOrienter", "avatarNumber" : "2", "tagLine": "I'm from a test!" } """ When I request "POST /api/programmers" Then the response status code should be 201 And the "nickname" property should equal "ObjectOrienter" https://knpuniversity.com/screencast/rest/testing 7 . 3

Slide 51

Slide 51 text

CHAKRAM describe("HTTP assertions", function () { it("should make HTTP assertions easy", function () { var response = chakram.get("http://httpbin.org/get?test=chakram"); expect(response).to.have.status(200); expect(response).to.have.header("content-type", "application/json"); expect(response).not.to.be.encoded.with.gzip; expect(response).to.comprise.of.json({ args: { test: "chakram" } }); return chakram.wait(); }); }); http://dareid.github.io/chakram/ 7 . 4

Slide 52

Slide 52 text

FRISBY var frisby = require('frisby'); frisby.create('Get Brightbit Twitter feed') .get('https://api.twitter.com/1/statuses/user_timeline.json?screen_name=brightbit') .expectStatus(200) .expectHeaderContains('content-type', 'application/json') .expectJSON('0', { user: { verified: false, location: "Oklahoma City, OK", url: "http://brightb.it" } }) .toss(); http://frisbyjs.com 7 . 5

Slide 53

Slide 53 text

DREDD (FOR API DOC) https://github.com/apiaryio/dredd 7 . 6

Slide 54

Slide 54 text

POSTMAN 7 . 7

Slide 55

Slide 55 text

HURL . IT 7 . 8

Slide 56

Slide 56 text

AND... : cURL-like tool for humans : like sed for JSON data You can add this to your .zshrc: HTTPie jq jsonapi() { http "$@" Accept:application/vnd.api+json \ Content-Type:application/vnd.api+json } 7 . 9

Slide 57

Slide 57 text

CONCLUSION 8 . 1

Slide 58

Slide 58 text

 DOCUMENTATION 1. Very important 2. Write it first 3. Test it 8 . 2

Slide 59

Slide 59 text

 CODE 1. I  JSON API 2. It is OK not to be 100% REST compliant 3. TEST YOUR API! 8 . 3

Slide 60

Slide 60 text

 TESTING 1. No excuse! 2. No excuse! 3. No excuse! 8 . 4

Slide 61

Slide 61 text

ONE MORE THING I did not talk about  security, but: API key / Authorization header are a good start OAuth is not for authentication, is! JWT ( ) is trendy Check out OpenID Connect LexikJWTAuthenticationBundle Auth0 article on Symfony 9

Slide 62

Slide 62 text

A: I DON'T KNOW. 10

Slide 63

Slide 63 text

THANK YOU. QUESTIONS?    williamdurand.fr github.com/willdurand @couac 11