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 and input errors? How to handle write operations and file uploads? 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 31, 2017
Tweet

Transcript

  1. @afilina API Tips From the Frontline IPC, Berlin - May

    31, 2017
  2. Objectives • Don't repeat my mistakes. • Overcome common obstacles.

    • Build elegant & pragmatic APIs.
  3. Anna Filina • Project rescue expert • Dev, trainer, speaker

  4. Endpoints What URL to call when

  5. Lists • /products • /products?category=games,movies • /products?include=platforms • /products?sort=-date •

    /products? pageNumber=2&pageSize=100 • /products?lang=fr
  6. Details • /products/1 • /products/1?include=photos,reviews

  7. Response Example: List { "data": [ {"name": "Skyrim"}, {"name": "Civilization

    V"} ], "meta": { "pages": 50, "count": 100 } }
  8. Verbs • POST /products • PUT or PATCH /products/1 •

    DELETE /products/1 • Version your APIs: ◦ api.example.org/v1/products ◦ Headers also an option.
  9. Request/response How to format stuff

  10. Multiple Formats • xml, json, html, etc. • Can use

    1 endpoint with Accept header. • Can send version, pagination & language info using headers too.
  11. Request • Bad: Content-Type: application/x-www- form-urlencoded • Content-Type: application/json •

    Send content (POST) in body, not headers.
  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 } }
  13. 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.
  14. Plupload var uploader = new plupload.Uploader({ runtimes: 'html5,html4', url: '/upload'

    }); uploader.init();
  15. Guzzle <?php $url = 'http://api.dev/profiles/1/edit'; $request = $client->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);
  16. Status Codes • Don't confuse API & HTTP codes. •

    2xx success. • 3xx redirect. • 4xx client error. • 5xx server error. • Send API-specific code in body.
  17. Example of Error Status: 400 Content-Type: application/json; charset=UTF-8 { "errors":

    [{ "code": 1001, "message": "Price must be greater than 0." }] }
  18. Testing Simpler than you think

  19. HTTP • Use Guzzle or built-in framework tools. • Generate

    HTTP request , compare output.
  20. 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()); // ... }
  21. Guzzle Test // ... $body = $response->getBody()->getContents(); $this->assertJsonStringEqualsJsonString('{ "data": {

    "id": "1", "name": "Overwatch", "price": 49.99 } }', $body);
  22. Testing Tips • Create separate database for tests. • Write

    tests before you code: ◦ TDD. ◦ Contract between you and client dev.
  23. Authentication Ugh, THAT part

  24. 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.
  25. OAuth2 • SSL required. • Advanced features, like access scope.

    • Can be overkill if you need basic features. • Private credentials.
  26. OAuth2 - Conceptual Diagram User Client app
 (php/js/mobile) API Request

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

    No replay (nonce). • Comes out-of-the-box with some frameworks.
  28. Digest - Conceptual Diagram Request User/client app API Unauthorized Validate

    digest (nonce,pass) Request nonce
  29. Refactoring to API Don't rewrite all the legacy at once

  30. 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.
  31. Services vs Libraries • Services: GET /products?category=game • Libraries/classes: ◦

    $table->getApiList( ["category" => "game"] ); ◦ Bypass HTTP ◦ Can fetch without pagination. ◦ Can hydrate to models.
  32. Implementation tips Is this a good way to code it?

  33. Tools • I use MVC frameworks & ORMs. • Aside

    from frameworks, I prefer small libraries that don't do too much magic.
  34. Reuse & Standardize • Goal: streamline endpoint creation. • Automate

    request parameters extraction. • Automate query generation based on parameters. • Keep things fully customizable. • Decouple from framework.
  35. Performance Make things faster. Much faster.

  36. Benchmark • Give Tideways or Blackfire a spin. • Make

    performance part of your test suite. • In dev/test mode, use a perf block.
  37. Example { "perf": { "db_time": 0.00367, "total_time": 0.3120, "memory": 19661

    } }
  38. 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.
  39. Performance Tips • Use API keys even for public endpoints

    (DDoS mitigation). • Put stuff in Memcached/Redis (especially blocked keys). • HTTP server can check headers.
  40. 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
  41. @afilina afilina.com joind.in