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
Example of error Status: 400 Content-Type: application/json; charset=UTF-8 { "error": { "code": 1001, "message": "Price must be greater than 0." } } 17
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
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
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
Reuse & standardize • Goal: streamline endpoint creation • Base controller for common request processing • Base repository for querying with filters • Keep things fully customizable 34
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
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