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

Dr Evil’s World Domination API

Dr Evil’s World Domination API

Your API’s developer experience (DX) matters as much as your web site’s user experience: making people more successful ultimately makes more people successful. For HTTP APIs, a good DX doesn’t only come from HTTP’s simplicity, but also from its depth. This presentation introduces a parody API that makes every classic mistake, and shows how they lead to Dr Evil’s inevitable downfall.

Further reading: Use HTTP error handling in APIs and HTTP client error status code checklist.

Peter Hilton

April 08, 2021
Tweet

More Decks by Peter Hilton

Other Decks in Technology

Transcript

  1. I want two* things: 1. good HTTP API docs 2.

    good HTTP APIs * good DX 2 @PeterHilton •
  2. Dr Evil’s big mistakes that will lead to his inevitable

    downfall 1. Building an application whose client communicates via HTTP with a back-end, and using that as a public API. 2. Publishing a public API without documentation. 3. Not hiring a technical writer to write the customer-facing documentation. 4. Not improving the API based on technical writer feedback. 5. Not expecting passive-aggressive API documentation. 5 @PeterHilton •
  3. Retrieve evil plan Fetches the specified world domination plan. GET

    /plans/world-domination/042 HTTP/1.1 Accept: text/xml HTTP/1.1 200 OK Content-Type: text/xml <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document xmlns:ve="http://schemas.openxmlformats.org/markup-compatibility/2006 " xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r=“http://schemas.openx mlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlf ormats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmln s:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxml formats.org/wordprocessingml/2006/main" xmlns:wne="http://schemas.microsoft.com/o ffice/word/2006/wordml"><w:body><w:tbl><w:tblPr><w:tblStyle w:val=“Tabelraster"/> <w:tblW w:w="9889" w:type="dxa"/><w:tblLook w:val=“04A0"/></w:tblPr><w:tblGrid><w :gridCol w:w="2943"/><w:gridCol w:w="1663"/><w:gridCol w:w="1535"/><w:gridCol w:w
  4. ⚠ If the request URL is not valid, the response

    status is 500 Server Error instead of 404 Not Found. Retrieve evil plan Fetches the specified world domination plan. GET /plans/world-domination/42 HTTP/1.1 Accept: text/xml HTTP/1.1 500 Internal Server Error Content-Type: text/xml <?xml version="1.0" encoding="utf-8" ?> <error> <code>666_INVALID_EVIL_PLAN_ID</code> </error>
  5. ⚠ A valid request URL with the wrong HTTP method

    returns a 500 Server Error status instead of 405 Method Not Allowed. Delete evil plan Discontinue a world domination plan. DELETE /plans/world-domination/001 HTTP/1.1 Accept: text/xml HTTP/1.1 500 Internal Server Error Content-Type: text/xml <?xml version="1.0" encoding="utf-8" ?> <error> <code>666_DELETE_EVIL_PLANS_WITH_POST</code> </error>
  6. ⚠ Don’t expect to receive a 400 Bad Request status

    for an invalid request. You’re probably going to get 500 Server Error, or maybe 200 OK. Execute evil plan Fetches the specified world domination plan. POST /plans/world-domination/001 HTTP/1.1 Accept: text/xml HTTP/1.1 200 OK Content-Type: text/xml <?xml version="1.0" encoding="utf-8" ?> <error> <code>666_ERROR_ERROR_ERROR</code> </error>
  7. " You might expect that posting a plan in the

    wrong format would result in a 415 Unsupported Media Type status, but it turns out that you’ll just get a server error. Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: application/json Accept: application/json { "summary": "World domination" } HTTP/1.1 500 Internal Server Error Content-Type: text/xml <?xml version="1.0" encoding="utf-8" ?> <error> <code>666_CANNOT_PARSE_EVIL_PLAN</code> </error>
  8. # Creating a new evil plan returns a 200 OK

    status instead of 201 Created. The (partial) URL is in the response body instead of a Location header. Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: text/xml Accept: text/xml <?xml version="1.0" encoding="utf-8" ?> <plan> <summary>World domination</summary> </plan> HTTP/1.1 200 OK Content-Type: text/xml <?xml version="1.0" encoding="utf-8" ?> <success> <location id="/plans/world-domination/002"/> </success>
  9. $ Our back-end framework is rubbish, but at least the

    right status is in the JSON Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: text/xml Accept: application/json <?xml version="1.0" encoding="utf-8" ?> <plan> <summary>World domination</summary> </plan> HTTP/1.1 200 OK Content-Type: application/json { "status":201, "message":"Created", "location":"world-domination/002" }
  10. % Let’s be realistic - the API is just going

    to ignore your Accept request header, and always give you XML (or worse) instead of using 406 Not Acceptable Good luck writing error- handling code for that! Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: text/xml Accept: application/json <?xml version="1.0" encoding="utf-8" ?> <plan> <summary>World domination</summary> </plan> HTTP/1.1 200 OK Content-Type: text/xml <?xml version="1.0" encoding="utf-8" ?> <success> <location id="world-domination/02"/> </success>
  11. & The response body is a JSON array… in random

    order. List evil plans Lists all published plans. GET /plans HTTP/1.1 Accept: application/json HTTP/1.1 200 OK Content-Type: application/json [ {"id":"001","title":"World domination"} {"id":"007","title":"Kill Bond"} {"id":"003","title":"Make everyone use SOAP"} {"id":"004","title":"Teach UML in schools"} {"id":"002","title":"Defend the patriarchy"} {"id":"006","title":"Roll out SAFe"} {"id":"008","title":"Mandate open plan offices"} {"id":"005","title":"Something with blockchain"} ]
  12. ' The request requires JSON embedded in form encoding. (and

    we’ve fixed most of the encoding bugs) Create evil plan Publishes a new world domination plan. POST /plans HTTP/1.1 Content-type: text/xml Accept: application/json title=World%20domination& plan=%7B%22summar%22%3A%22world+domination%22%2C %22scope%22%3A%22WORLD%22%2C%22owner%22%3A%22Dr+ Evil%22%7D HTTP/1.1 201 Created Location: /plans/world-domination/02
  13. ( Do you think this API is going to give

    you a generic 400 Bad Request error for an application-specific non-HTTP client error. No it is not. You’ll have to guess whether it’s worth retrying this request later. Create evil plan Publishes a new world domination plan. POST /plans/world-domination HTTP/1.1 Content-type: text/xml Accept: text/xml <?xml version="1.0" encoding="utf-8" ?> <plan> <summary>World peace</summary> </plan> HTTP/1.1 500 Internal Server Error Content-Type: text/xml <?xml version="1.0" encoding="utf-8" ?> <error> <code>666_INSUFFICIENTLY_EVIL_PLAN</code> </error>
  14. Expose API ugliness When you show the raw HTTP session,

    API ugliness has nowhere to hide. When you abstract away the HTTP layer, you tend to end up with a degenerate API. This is one reason why the API your own application’s front- end uses isn’t going to make a good public API. 21 @PeterHilton •
  15. Use the right HTTP response status codes The first step

    in good HTTP API error handling is to actually use the right HTTP response status codes. Clients need the right HTTP status to decide what to do next, e.g. retry after a server error, not after a client error. Clients also need status codes when they can’t parse the response body. 22 @PeterHilton •
  16. Read Peter’s blog posts Use HTTP error handling in APIs

    hilton.org.uk/blog/http-error-handling Client error status code checklist hilton.org.uk/blog/http-client-error-checklist 23 @PeterHilton •