API Standards 2.0

API Standards 2.0

We’re all familiar with things like HTTP codes and content types, but there’s so much more we can do when developing an API to make life easier for consumers. How many times have you used an API only to find out that every endpoint is slightly different – some use `snake_case`, others `camelCase`, sometimes the field is called `id`, sometimes it’s `user_id`. How about pagination? Error responses? What about API documentation?

Trying to standardise on all these things can kill an engineering team. There are so many options out there it’s difficult to know where to start. Come along and learn what works for our team! We’ll cover contentious topics (should the version be in the URL or a header?), lesser-known standards that are great (RFC 7807 springs to mind) and a couple of things that aren’t an issue right up until they’re a really big issue (like pagination).

Bbf9decfbfc2ab5b450ec503749ded28?s=128

Michael Heap

October 04, 2018
Tweet

Transcript

  1. API STANDARDS 2.0

  2. HELLO! I 'm Michael I'm @mheap on Twitter I have

    a favourite Time Zone (DMT)
  3. API STANDARDS 2.0

  4. API STANDARDS 2.0 AN OPINIONATED GUIDE TO

  5. 5

  6. APIs at Nexmo 6 REST-like

  7. APIs at Nexmo 7 Pragmatic REST-like

  8. APIs at Nexmo 8 Pragmatic REST-like Consumer Success

  9. API Standards 9 Status Codes

  10. API Standards 10 HTTP Verbs Status Codes

  11. API Standards 11 HTTP Verbs Status Codes Nouns, not Verbs

  12. What we're not going to cover ▸ Message formats ▸

    Rate limiting ▸ Content negotiation ▸ HTTP headers ▸ Hypermedia ▸ Resource design ▸ Response design ▸ RPC endpoints ▸ Documentation ▸ Asynchronous actions ▸ Idempotency ▸ Caching ▸ API Gateways ▸ Security ▸ GraphQL ▸ Available RFCs 12
  13. 1. Errors Status Codes, RFC7807 + Extensions 13

  14. 2. Versioning Header/URL, Versioning Schemes, Deprecation 14

  15. 3. Collection Management Sorting, Filtering, Searching, Pagination 15

  16. 4. URI Design Subresources, Action endpoints 16

  17. 1. Errors Status Codes, RFC7807 + Extensions 17

  18. “ Errors are easy! People should just use status codes

  19. 401 Unauthorized Error Design 19

  20. 401 Unauthorized { "error": "No credentials provided" } Error Design

    20
  21. 401 Unauthorized { "error": "Invalid credentials" } Error Design 21

  22. 401 Unauthorized { "error": "Invalid credentials", "more_help": "http://example.com/ auth#invalid-credentials" }

    Error Design 22
  23. 401 Unauthorized { "msg": "Invalid credentials" } Error Design 23

  24. 401 Unauthorized { "errorCode": 118118, "errorMessage": "Invalid credentials" } Error

    Design 24
  25. 401 Unauthorized "Invalid credentials" Error Design 25

  26. RFC7807 Type A URI reference that identifies the problem type.

    When followed, it provides human-readable documentation for the problem type Title A short, human-readable summary of the problem type Detail A human- readable explanation specific to this occurrence of the problem 26 Instance A URI reference that identifies the specific occurrence of the problem
  27. RFC7807 Type A URI reference that identifies the problem type.

    When followed, it provides human-readable documentation for the problem type Title A short, human-readable summary of the problem type Detail A human- readable explanation specific to this occurrence of the problem 27 Instance A URI reference that identifies the specific occurrence of the problem Status The HTTP status code generated by the origin server for this occurrence of the problem.
  28. RFC7807 Type A URI reference that identifies the problem type.

    When followed, it provides human-readable documentation for the problem type Title A short, human-readable summary of the problem type Detail A human- readable explanation specific to this occurrence of the problem 28 Instance A URI reference that identifies the specific occurrence of the problem
  29. { "type": "https://developer.nexmo.com/api- errors#unauthorized", "title": "Invalid credentials supplied", "detail": "You

    did not provide correct credentials.", "instance": "797a8f199c45014ab7b08bfe9cc1c12c" } RFC7807 29
  30. 403 Forbidden { "type": "https://example.com/probs/out-of- credit", "title": "You do not

    have enough credit.", "detail": "Your current balance is 30, but that costs 50.", "instance": "/account/12345/msgs/abc" } Extension Members 30
  31. 403 Forbidden { "type": "https://example.com/probs/out-of-credit", "title": "You do not have

    enough credit.", "detail": "Your current balance is 30, but that costs 50.", "instance": "/account/12345/msgs/abc", "balance": 30, "accounts": ["/account/12345", "/account/67890"] } Extension Members 31
  32. 400 Bad Request { "type": "https://developer.nexmo.com/api-errors/account/secret- management#validation", "title": "Bad Request",

    "detail": "The request failed due to validation errors", "invalid_parameters": [ { "name": "secret", "reason": "must contain 1 upper case character" } ], "instance": "797a8f199c45014ab7b08bfe9cc1c12c" } Extension Members 32
  33. 401: No Credentials, Invalid Credentials 403: Feature disabled, Exceeded calls-per-second

    limit 422: Invalid product specified + more! Response Library 33
  34. “ As a consumer of the APIs, this has already

    dramatically reduced the amount of code I have to write for each endpoint, and I can't wait until all endpoints are standardised
  35. 2. Versioning Header/URL, Versioning Schemes, Deprecation 35

  36. “ The reason to make a real REST API is

    to get evolvability … a "v1" is a middle finger to your API customers, indicating RPC/HTTP (not REST) https://twitter.com/fielding/status/376835835670167552
  37. “With a sufficient number of users of an interface, it

    doesn’t matter what you promised in the interface contracts, all observable behaviors of your class or function or whatnot will be depended upon by somebody. Hyrum's Law
  38. Accept: application/vnd.nexmo+json; version=3 URI vs Header 38

  39. Accept: application/vnd.nexmo+json; version=3 
 X-Nexmo-Version: 3 URI vs Header 39

  40. Accept: application/vnd.nexmo+json; version=3 
 X-Nexmo-Version: 3 https://api.nexmo.com/v3 URI vs Header

    40
  41. https://api.nexmo.com/v1/calls Global vs Endpoint versioning 41

  42. https://api.nexmo.com/v1/calls https://api.nexmo.com/v1/media Global vs Endpoint versioning 42

  43. https://api.nexmo.com/v1/calls https://api.nexmo.com/v3/media Global vs Endpoint versioning 43

  44. https://api.nexmo.com/v1/calls Versioning Scheme 44

  45. https://api.nexmo.com/v1/calls https://api.example.com/2010-04-01 Versioning Scheme 45

  46. https://api.nexmo.com/v1/calls https://api.example.com/2010-04-01 https://api.example.com/1.2.3 Versioning Scheme 46

  47. https://api.nexmo.com/v3.new- feature/calls Version Variants 47

  48. Have a deprecation policy. It doesn't matter what it is,

    but be consistent. Deprecation Policies 48
  49. When the decision has been taken to deprecate an API:

    • For beta products the deprecation period must be at least 30 days (60 days recommended) • For GA products the deprecation period must be at least 1 year • Warning emails will be sent to the API at regular intervals before the deprecation time • A guide will be supplied to customers explaining how to migrate to the replacement API with the initial deprecation notice. Deprecation Policies: Nexmo 49
  50. Use the Sunset Header Sunset: Sat, 31 Dec 2018 23:59:59

    GMT Sunset Header 50 https://tools.ietf.org/html/draft-wilde-sunset-header-03
  51. Use the Link Header Link: <https://example.com/docs/foo- shutdown> rel="sunset" Link Header

    51
  52. 3. Collection Management Sorting, Filtering, Searching, Pagination 52

  53. Sorting 53

  54. GET /users? sort_by=email&order_by=asc Sorting 54

  55. GET /users?sort_by=email.asc Sorting 55

  56. GET /users?sort_by=email.asc &sort_by=status.desc Sorting 56

  57. GET /users? sort_by=email.asc,status.desc Sorting 57

  58. GET /users?sort_by=+email,-status Sorting 58

  59. GET /users?sort_by=+email,-status GET /users? sort_by=email.asc,status.desc Sorting 59

  60. Filtering 60

  61. Filtering 61 Discrete data e.g. user role Continuous data e.g.

    subscription expiry time
  62. Discrete Data 62 /users?role=admin /orders?shipped=true /calls?status=active

  63. Discrete Data 63 /users?role=admin /orders?shipped=true /calls?status=active 403 Forbidden /users?is_fbi_informant=true

  64. Discrete Data: JSON API 64 GET /employees? filter[role]=internal&filter[title]= senior

  65. Discrete Data: JSON API 65 GET /employees? filter[role]=internal&filter[title]= senior GET

    /employees? role=internal&title=senior
  66. Continuous Data 66

  67. Continuous Data 67 GET /orders? start_date=2018-01-01&end- date=2018=01-31

  68. Continuous Data 68 GET /orders? start_date=2018-01-01&end- date=2018=01-31 GET /orders? date[gte]=2018-01-01&date[lte]=2018=

    01-31
  69. Industry Standards 69

  70. Industry Standards: SCIM 70 filter=userName eq "bjensen" filter=name.familyName co "O'Malley"

    filter=userName sw "J" filter=urn:ietf:params:scim:schemas:core: 2.0:User:userName sw "J" filter=title pr filter=meta.lastModified gt "2011-05-13T04:42:34Z" https://tools.ietf.org/html/rfc7644#section-3.4.2.2
  71. Searching 71

  72. Searching 72 GET /items?q=title:red chair AND price:[10 TO 100]

  73. Pagination 73 Offset based Cursor based

  74. Pagination: Offset 74 GET /calls?page=3&page_size=100

  75. Pagination: Cursor 75 GET /calls GET /calls? cursor=2018-01-19T12:33:51 GET /calls?

    cursor=2018-01-18T03:00:18
  76. Pagination: Links 76 { "_links": { "next": { "href": "/calls?cursor=9274"

    } } } Link: <https://api.nexmo.com/v1/calls? cursor=9274>; rel="next",
  77. 4. URI Design Subresources, Action endpoints 77

  78. GET /users/1 Subresources 78

  79. GET /users/1 GET /users/1/calls Subresources 79

  80. GET /users/1 GET /users/1/calls GET /users/1/calls/JSDB-1837A Subresources 80

  81. GET /users/1 GET /users/1/calls GET /users/1/calls/JSDB-1837A GET /calls/JSDB-1837A Subresources 81

  82. GET /users/1 GET /users/1/calls Subresources 82

  83. GET /users/1 GET /users/1/calls GET /calls?user=1 Subresources 83

  84. GET /users/1 GET /addresses/33-90210 GET /users/1/addresses/33-90210 Subresources 84

  85. GET /users/1 GET /addresses/33-90210 GET /users/1/addresses/33-90210 GET /user_addresses/1-33-90210 Subresources 85

  86. POST /machines/1/shutdown Actions 86

  87. POST /machines/1/shutdown POST /machines/1/actions/shutdown Actions 87

  88. POST /machines/1/shutdown POST /machines/1/actions/shutdown POST /machines/1/action:shutdown Actions 88

  89. POST /machines/1/shutdown POST /machines/1/actions/shutdown POST /machines/1/action:shutdown POST /machines/1/actions {"type": "shutdown"}

    Actions 89
  90. POST /machine-shutdown Actions as Resources 90

  91. POST /machine-shutdown PUT /article-locks/{article-id} DELETE /article-locks/{article-id} POST /balance-transfer Actions as

    Resources 91
  92. Conclusion

  93. What we didn't cover ▸ Message formats ▸ Rate limiting

    ▸ Content negotiation ▸ HTTP headers ▸ Hypermedia ▸ Resource design ▸ Response design ▸ RPC endpoints ▸ Documentation ▸ Asynchronous actions ▸ Idempotency ▸ Caching ▸ API Gateways ▸ Security ▸ GraphQL ▸ Available RFCs 93
  94. @mheap m@michaelheap.com