Slide 1

Slide 1 text

@afilina API Tips From the Frontline IPC, Berlin - May 31, 2017

Slide 2

Slide 2 text

Objectives • Don't repeat my mistakes. • Overcome common obstacles. • Build elegant & pragmatic APIs.

Slide 3

Slide 3 text

Anna Filina • Project rescue expert • Dev, trainer, speaker

Slide 4

Slide 4 text

Endpoints What URL to call when

Slide 5

Slide 5 text

Lists • /products • /products?category=games,movies • /products?include=platforms • /products?sort=-date • /products? pageNumber=2&pageSize=100 • /products?lang=fr

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Response Example: List { "data": [ {"name": "Skyrim"}, {"name": "Civilization V"} ], "meta": { "pages": 50, "count": 100 } }

Slide 8

Slide 8 text

Verbs • POST /products • PUT or PATCH /products/1 • DELETE /products/1 • Version your APIs: ◦ api.example.org/v1/products ◦ Headers also an option.

Slide 9

Slide 9 text

Request/response How to format stuff

Slide 10

Slide 10 text

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

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.

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": "Overwatch", "price": 49.99 } }

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

Plupload var uploader = new plupload.Uploader({ runtimes: 'html5,html4', url: '/upload' }); uploader.init();

Slide 15

Slide 15 text

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

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.

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Testing Simpler than you think

Slide 19

Slide 19 text

HTTP • Use Guzzle or built-in framework tools. • Generate HTTP request , compare output.

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

Guzzle Test // ... $body = $response->getBody()->getContents(); $this->assertJsonStringEqualsJsonString('{ "data": { "id": "1", "name": "Overwatch", "price": 49.99 } }', $body);

Slide 22

Slide 22 text

Testing Tips • Create separate database for tests. • Write tests before you code: ◦ TDD. ◦ Contract between you and client dev.

Slide 23

Slide 23 text

Authentication Ugh, THAT part

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.

Slide 25

Slide 25 text

OAuth2 • SSL required. • Advanced features, like access scope. • Can be overkill if you need basic features. • Private credentials.

Slide 26

Slide 26 text

OAuth2 - Conceptual Diagram 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.

Slide 28

Slide 28 text

Digest - Conceptual Diagram 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/module at a time. • Start with whatever has fewer dependencies (or most critical). • Delete dead code. • Copy production data for dev environment.

Slide 31

Slide 31 text

Services vs Libraries • Services: GET /products?category=game • Libraries/classes: ◦ $table->getApiList( ["category" => "game"] ); ◦ Bypass HTTP ◦ Can fetch without pagination. ◦ Can hydrate to models.

Slide 32

Slide 32 text

Implementation tips Is this a good way to code it?

Slide 33

Slide 33 text

Tools • I use MVC frameworks & ORMs. • Aside from frameworks, I prefer small libraries that don't do too much magic.

Slide 34

Slide 34 text

Reuse & Standardize • Goal: streamline endpoint creation. • Automate request parameters extraction. • Automate query generation based on parameters. • Keep things fully customizable. • Decouple from framework.

Slide 35

Slide 35 text

Performance Make things faster. Much faster.

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 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.

Slide 39

Slide 39 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.

Slide 40

Slide 40 text

Useful Links • Standard API format
 http://jsonapi.org/format/ • Digest implementation
 http://php.net/manual/en/features.http- auth.php • Book "Build APIs You Won't Hate"
 https://leanpub.com/build-apis-you- wont-hate

Slide 41

Slide 41 text

@afilina afilina.com joind.in