API Tips From the Frontline

API Tips From the Frontline

Starting to write an API is an easy task, but you quickly stumble upon many obstacles and hard decisions. How to manage result pagination? How to handle write operations, file uploads and authentication? Join me as I share my tricks that allowed me to ship high-profile projects in record time while keeping the code clean and maintainable.

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina

May 29, 2016
Tweet

Transcript

  1. 2.

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

    Overcome common obstacles. • Build elegant & pragmatic APIs. 2
  2. 7.
  3. 8.

    Write • POST /products • PUT or PATCH /products/1 •

    DELETE /products/1 • Version your APIs: ◦ api.example.org/v1/products ◦ Headers also an option. 8
  4. 10.

    Multiple formats • xml, json, html, etc. • Can use

    1 endpoint with Accept header. • Can send version, pagination & language info using headers too. 10
  5. 12.

    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 } } 12
  6. 13.

    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. 13
  7. 14.

    LIMIT SELECT * FROM game LEFT JOIN rating ON rating.game_id

    = game.id WHERE game.price < 20 LIMIT 2; 14 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
  8. 15.

    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. 15
  9. 16.

    Plupload var uploader = new plupload.Uploader({ runtimes: 'html5,html4', max_file_size: '5mb',

    url: '/api/upload', filters: [{extensions: 'jpg,png,jpeg'}] }); uploader.init(); 16
  10. 17.

    Guzzle <?php $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); 17
  11. 18.

    Status codes • Don't confuse API & HTTP codes. •

    2xx success. • 3xx redirect. • 4xx client error. • 5xx server error. • Send API-specific code in body. 18
  12. 19.

    Example of error Status: 400 Content-Type: application/json; charset=UTF-8 { "errors":

    {[ "code": 1001, "message": "Price must be greater than 0." ]} } 19
  13. 22.

    Guzzle test // tests/ApiProductTest.php public function test_GetOneProduct_ReturnsSuccess() { $client =

    new Client(); $response = $client->get('http://example.org/products/1', [ 'exceptions' => false, 'headers' => ['Accept' => 'application/json'] ]); $this->assertEquals(200, $response->getStatusCode()); // ... } 22
  14. 24.

    Testing tips • Create separate database for tests. • Write

    tests before you code: ◦ TDD. ◦ Contract between you and client dev. 24
  15. 26.

    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. 26
  16. 27.

    OAuth2 • SSL required (can be risky). • Advanced features

    like access scope. • Can be overkill if you need basic features. • Private credentials. 27
  17. 28.

    OAuth2 - conceptual diagram 28 User Client app
 (php/js/mobile) API

    Request Forward Validate Create token Store token Login form Login token user/pass
  18. 29.

    Digest • Its own encryption. • Easy to implement. •

    No replay (nonce). • Comes out-of-the-box with some frameworks. 29
  19. 30.
  20. 32.

    Progressive rewrite • Rewrite one component/module at a time. •

    Start with whatever has fewer dependencies (or critical). • Delete dead code. • Copy production data for dev environment. 32
  21. 33.

    Services vs libraries • Services: GET /products?id=1,5 • Libraries /classes:

    • Bypass HTTP • $table->getApiList( ["id" => "1,5"] ); • Can fetch without pagination. • Can hydrate to models. 33
  22. 35.

    Libraries • I use MVC frameworks & ORMs. • Aside

    from frameworks, I prefer small tools that don't do too much magic. 35
  23. 36.

    Reuse & standardize • Goal: streamline endpoint creation. • Automate

    request parameters extraction. • Automate query generation based on parameters. • Keep things fully customizable. • Decouple from framework. 36
  24. 38.

    Benchmark • Give Tideways or Blackfire a spin. • Make

    performance part of your test suite. • In dev/test mode, use a meta block. 38
  25. 40.

    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. 40
  26. 41.

    Performance tips • Use API keys even for public endpoints

    (DDoS mitigation). • Put stuff in Memcached/Redis (especially blocked keys). • HTTP server can check headers. 41
  27. 42.

    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 42
  28. 43.

    Anna Filina • Development. • Fix bugs & performance issues.

    • Workshops on testing, frameworks & APIs. • Advisor on testing strategy, legacy code. 43