Upgrade to Pro — share decks privately, control downloads, hide ads and more …

API Tips from the Frontline

API Tips from the Frontline

Anna Filina

June 09, 2015
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript

  1. What you will learn • Don't repeat my mistakes •

    Overcome common obstacles • Build elegant & pragmatic APIs 4
  2. 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 } } 9
  3. Response example: list { "data": [ {"name": "Skyrim"}, {"name": "Civilization

    V"} ], "page": { "number": 1, "size": 2, "pages": 50, "total": 100 } } 10
  4. 3 queries • Count query with same filters but no

    pagination • Query with filters, pagination & sorting, but only select DISTINCT IDs ◦ Because JOIN + LIMIT = wrong number of root records • Query all the data (with joins) by IDs and apply sorting 11
  5. LIMIT SELECT * FROM game LEFT JOIN rating ON rating.game_id

    = game.id WHERE game.price < 20 LIMIT 2; 12 id name price game_id score 1 Skyrim 19.99 1 9.1 1 Skyrim 19.99 1 8.9 2 Diablo 3 17.89 2 8.5
  6. 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
  7. Plupload var uploader = new plupload.Uploader({ runtimes: 'html5,html4', max_file_size: '5mb',

    url: '/api/upload', filters: [{extensions: 'jpg,png,jpeg'}] }); uploader.init(); 14
  8. Guzzle <? $url = 'http://example.org/profiles/1/edit'; $request = $client->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
  9. 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
  10. Example of error Status: 400 Content-Type: application/json; charset=UTF-8 { "error":

    { "code": 1001, "message": "Price must be greater than 0." } } 17
  11. 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
  12. Testing tips • Create separate database for tests • Write

    tests before you code: • TDD • Contract between you and client dev 22
  13. 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
  14. OAuth2 • SSL required (can be risky) • Advanced features

    like access scope • Can be overkill if you need basic features • Private credentials 25
  15. OAuth2 - conceptual diagram 26 User Client app
 (php/js/mobile) API

    Request Forward Validate Create token Store token Login form Login token user/pass
  16. Digest • Its own encryption • Easy to implement •

    No replay (nonce) • Comes out-of-the-box with some frameworks 27
  17. 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
  18. Services vs libraries • Services: GET /products?id=1,5 • Libraries: •

    Bypass HTTP • $db->getList( ["id" => "1,5"] ); • Can fetch without pagination • Can hydrate to models 31
  19. Libraries • I use Symfony & Doctrine • Aside from

    frameworks, I prefer small tools that don't do too much magic 33
  20. Reuse & standardize • Goal: streamline endpoint creation • Base

    controller for common request processing • Base repository for querying with filters • Keep things fully customizable 34
  21. Benchmark • Give Tideways or Blackfire a spin • Make

    performance part of your test suite • In dev/test mode, use a meta block 36
  22. 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. • Use API keys even for public endpoints (DDoS mitigation). • Put stuff in Memcached/Redis (especially blocked keys). • HTTP server can check headers 38
  23. 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
  24. Anna Filina • Development: PHP, JS, etc. • Fix problems:

    bugs, performance, etc. • Workshops: testing, Symfony, AngularJS, API, etc. • Advisor: testing strategy, legacy code, etc. 40