Slide 1

Slide 1 text

foolab.ca | @foolabca API Tips From the Frontline BEPHPUG, Berlin - June 9, 2015

Slide 2

Slide 2 text

Anna Filina • Developer • Problem solver • Teacher • Advisor • FooLab + ConFoo 2

Slide 3

Slide 3 text

What you will learn • Don't repeat my mistakes • Overcome common obstacles • Build elegant & pragmatic APIs 3

Slide 4

Slide 4 text

Endpoints What URL to call when

Slide 5

Slide 5 text

Lists • /products • /products?category=games,movies&lang=fr • /products?include=platforms • /products?sort=-date • /products?page[number]=2&page[size]=100 • /products?lang=fr 5

Slide 6

Slide 6 text

Details • /products/1 • /products/1?include=photos,reviews 6

Slide 7

Slide 7 text

Response example: list { "data": [ {"name": "Skyrim"}, {"name": "Civilization V"} ], "page": { "number": 1, "size": 2, "pages": 50, "total": 100 } } 7

Slide 8

Slide 8 text

Write • POST /products • PUT or PATCH /products/1 • DELETE /products/1 • Version your APIs: api.example.org/v1/products 8

Slide 9

Slide 9 text

Request/Response How to format stuff

Slide 10

Slide 10 text

Multiple formats • xml, json, etc. • Can use 1 endpoint with Accept header • Can send version, pagination & language info using headers too 10

Slide 11

Slide 11 text

Request • Bad: Content-Type: application/x-www-form-urlencoded • Content-Type: application/json • Send content (POST) in body, not headers 11

Slide 12

Slide 12 text

Request example: add POST /products HTTP/1.1 Host: api.example.org Content-Type: application/json; charset=UTF-8 { "data": { "name": "Skyrim", "price": 19.99 } } 12

Slide 13

Slide 13 text

Upload • Use tools, don't DIY (do it yourself). • Client-side: Symfony • Client-side (more dynamic): Plupload • Step 1: upload temp file. • Step 2: give file path to next API request. • Server-side: Guzzle. 13

Slide 14

Slide 14 text

Plupload var uploader = new plupload.Uploader({ runtimes: 'html5,html4', max_file_size: '5mb', url: '/api/upload', filters: [{extensions: 'jpg,png,jpeg'}] }); uploader.init(); 14

Slide 15

Slide 15 text

Guzzle createRequest('PATCH', $url); $reqBody = $request->getBody(); $reqBody->setField('data', ['first_name':'Anna']); $file = new PostFile('i.jpg', fopen('/path', 'r')); $reqBody->addFile($file); $response = $client->send($request); 15

Slide 16

Slide 16 text

Status codes • Don't confuse API & HTTP codes • 2xx success • 3xx redirect • 4xx client error • 5xx server error • Send API-specific code in body 16

Slide 17

Slide 17 text

Example of error Status: 400 Content-Type: application/json; charset=UTF-8 { "error": { "code": 1001, "message": "Price must be greater than 0." } } 17

Slide 18

Slide 18 text

Testing Simpler than you think

Slide 19

Slide 19 text

HTTP • Use Guzzle or built-in framework tool • Generate HTTP request , compare output 19

Slide 20

Slide 20 text

Guzzle test // tests/ApiProductTest.php public function testGetOneProduct() { $client = new Client(); $response = $client->get('http://example.org/products/1', [ 'exceptions' => false, 'headers' => ['Accept' => 'application/json'] ]); $this->assertEquals(200, $response->getStatusCode()); // ... } 20

Slide 21

Slide 21 text

Guzzle test // ... $body = $response->getBody()->getContents(); $this->assertJsonStringEqualsJsonString('{ "data": { "id": "1", "name": "Skyrim", "price": 19.99 } }', $body); 21

Slide 22

Slide 22 text

Testing tips • Create separate database for tests • Write tests before you code: • TDD • Contract between you and client dev 22

Slide 23

Slide 23 text

Authentication Nooo, I hate that part! Someone else code it plz.

Slide 24

Slide 24 text

Multiple methods • Don't send username/password in each request ◦ Especially with untrusted 3rd parties ◦ Especially if no SSL • You can have multiple auth methods for one API • Sessions similar to tokens 24

Slide 25

Slide 25 text

OAuth2 • SSL required (can be risky) • Advanced features like access scope • Can be overkill if you need basic features • Private credentials 25

Slide 26

Slide 26 text

OAuth2 - conceptual diagram 26 User Client app
 (php/js/mobile) API Request Forward Validate Create token Store token Login form Login token user/pass

Slide 27

Slide 27 text

Digest • Its own encryption • Easy to implement • No replay (nonce) • Comes out-of-the-box with some frameworks 27

Slide 28

Slide 28 text

Digest - conceptual diagram 28 Request User/client app API Unauthorized Validate digest (nonce,pass) Request nonce

Slide 29

Slide 29 text

Refactoring to API Don't rewrite all the legacy at once

Slide 30

Slide 30 text

Progressive rewrite • Rewrite one component at a time • Start with whatever has fewer dependencies (or critical) • Delete dead code • Copy production data for dev environment 30

Slide 31

Slide 31 text

Implementation tips Is this a good way to code it?

Slide 32

Slide 32 text

Libraries • I use Symfony & Doctrine • Aside from frameworks, I prefer small tools that don't do too much magic 32

Slide 33

Slide 33 text

Reuse & standardize • Goal: streamline endpoint creation • Base controller for common request processing • Base repository for querying with filters • Keep things fully customizable 33

Slide 34

Slide 34 text

Performance Make things faster. Much faster.

Slide 35

Slide 35 text

Benchmark • Give Tideways or Blackfire a spin • Make performance part of your test suite • In dev/test mode, use a meta block 35

Slide 36

Slide 36 text

Example { "meta": { "perf": { "db_time": 0.00367, "total_time": 0.3120, "memory": 19661 } } } 36

Slide 37

Slide 37 text

Performance tips • Don't use lazy loading. Example: $product->getPhotos(). • Craft your own joins and carefully select fields. • Avoid ORM built-in hydration for read operations. 37

Slide 38

Slide 38 text

Performance tips • Use API keys even for public endpoints (DDoS mitigation). • Put stuff in Memcached/Redis (especially blocked keys). • HTTP server can check headers 38

Slide 39

Slide 39 text

Useful links • Standard API format
 http://jsonapi.org/format/ • Digest implementation
 http://php.net/manual/en/features.http-auth.php • Symfony components
 http://symfony.com/doc/current/components/index.html • Book "Build APIs You Won't Hate"
 https://leanpub.com/build-apis-you-wont-hate 39

Slide 40

Slide 40 text

Anna Filina • Development: PHP, JS, etc. • Fix problems: bugs, performance, etc. • Workshops: testing, Symfony, AngularJS, API, etc. • Advisor: testing strategy, legacy code, etc. 40

Slide 41

Slide 41 text

@afilina afilina.com