Slide 1

Slide 1 text

API STANDARDS 2.0

Slide 2

Slide 2 text

1. API Deprecation Before you build it, know how you're going to turn it off 2

Slide 3

Slide 3 text

API Deprecation 3 Beta Versions

Slide 4

Slide 4 text

API Deprecation 4 Major Versions Beta Versions

Slide 5

Slide 5 text

API Deprecation 5 Major Versions Beta Versions API
 Retirement

Slide 6

Slide 6 text

Have a deprecation policy. It doesn't matter what it is, but be consistent. Deprecation Policies 6

Slide 7

Slide 7 text

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 7

Slide 8

Slide 8 text

Use the Sunset Header Sunset: Sat, 31 Dec 2018 23:59:59 GMT Sunset Header 8 https://tools.ietf.org/html/draft-wilde-sunset-header-03

Slide 9

Slide 9 text

$ composer require hskrasek/guzzle-sunset --- $stack = new \GuzzleHttp\HandlerStack(\GuzzleHttp\choose_handler()); $stack->push(new \HSkrasek\Sunset\SunsetMiddleware($logger)); $client = new \GuzzleHttp\Client(['handler' => $stack]); API Deprecation: Sunset Header 9

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

“ 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

Slide 12

Slide 12 text

“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

Slide 13

Slide 13 text

Accept: application/vnd.nexmo+json; version=3 URI vs Header 13

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

3. HTTP Verbs GET, POST, PUT, DELETE, PATCH 23

Slide 24

Slide 24 text

GET /users 24

Slide 25

Slide 25 text

GET /users 25

Slide 26

Slide 26 text

GET /users 26

Slide 27

Slide 27 text

GET /users 27

Slide 28

Slide 28 text

GET /users 28

Slide 29

Slide 29 text

GET /users 29

Slide 30

Slide 30 text

GET /users 30 200 OK 301 Moved Permanently 302 Found 404 Not Found 406 Not Acceptable

Slide 31

Slide 31 text

POST /users 31

Slide 32

Slide 32 text

POST /users 32 201 Created 202 Accepted 415 Unsupported Media Type 422 Unprocessable Entity

Slide 33

Slide 33 text

PUT /users/1 33 200 OK

Slide 34

Slide 34 text

DELETE /users/1 34 204 No Content

Slide 35

Slide 35 text

PATCH /users/1 35

Slide 36

Slide 36 text

PATCH /users/1 36 RFC 7396 - JSON Merge Patch { "name":"Michael", "address": { "line_2": null } }

Slide 37

Slide 37 text

PATCH /users/1 37 RFC 6902 - JavaScript Object Notation (JSON) Patch [ { "op": "replace", "path": "/name", "value": "Michael" }, { "op": "remove", "path": "/ address/line_2" } ]

Slide 38

Slide 38 text

4. Resource Design URLs, HTTP Requests/Responses + Webhooks 38

Slide 39

Slide 39 text

GET /users GET /users/1 POST /users PUT /users/1 PATCH /users/1 DELETE /users/1 URL Design 39

Slide 40

Slide 40 text

GET /users/1 Subresources 40

Slide 41

Slide 41 text

GET /users/1 GET /users/1/calls Subresources 41

Slide 42

Slide 42 text

GET /users/1 GET /users/1/calls GET /users/1/calls/JSDB-1837A Subresources 42

Slide 43

Slide 43 text

GET /users/1 GET /users/1/calls GET /users/1/calls/JSDB-1837A GET /calls/JSDB-1837A Subresources 43

Slide 44

Slide 44 text

GET /users/1 GET /users/1/calls Subresources 44

Slide 45

Slide 45 text

GET /users/1 GET /users/1/calls GET /calls?user=1 Subresources 45

Slide 46

Slide 46 text

GET /users/1 GET /addresses/33-90210 GET /users/1/addresses/33-90210 Subresources 46

Slide 47

Slide 47 text

GET /users/1 GET /addresses/33-90210 GET /users/1/addresses/33-90210 GET /user_addresses/1-33-90210 Subresources 47

Slide 48

Slide 48 text

POST /machines/1/shutdown Actions 48

Slide 49

Slide 49 text

POST /machines/1/shutdown POST /machines/1/actions/shutdown Actions 49

Slide 50

Slide 50 text

POST /machines/1/shutdown POST /machines/1/actions/shutdown POST /machines/1/action:shutdown Actions 50

Slide 51

Slide 51 text

POST /machines/1/shutdown POST /machines/1/actions/shutdown POST /machines/1/action:shutdown POST /machines/1/actions {"type": "shutdown"} Actions 51

Slide 52

Slide 52 text

POST /machine-shutdown Actions as Resources 52

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Resource Design 54 • Use snake_case for key names • [resource]_id - A string that identifies a specific resource • [description]_url - A url that is necessary for communication. For example, callback_url. • [description]_method - Send the callback using either GET or POST. • start - The time the communication started in https:// en.wikipedia.org/wiki/ISO_8601 format. • end - The time the communication ended in https:// en.wikipedia.org/wiki/ISO_8601 format.

Slide 55

Slide 55 text

Webhooks 55 • Use snake_case for key names • start - The time the communication started in https:// en.wikipedia.org/wiki/ISO_8601 format. • end - The time the communication ended in https:// en.wikipedia.org/wiki/ISO_8601 format.

Slide 56

Slide 56 text

5. Errors Status Codes, RFC7807 + Extensions 56

Slide 57

Slide 57 text

“ Errors are easy! People should just use status codes

Slide 58

Slide 58 text

401 Unauthorized Error Design 58

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

401 Unauthorized { "error": "Invalid credentials" } Error Design 60

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

401 Unauthorized { "msg": "Invalid credentials" } Error Design 62

Slide 63

Slide 63 text

401 Unauthorized { "errorCode": 118118, "errorMessage": "Invalid credentials" } Error Design 63

Slide 64

Slide 64 text

401 Unauthorized "Invalid credentials" Error Design 64

Slide 65

Slide 65 text

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 65 Instance A URI reference that identifies the specific occurrence of the problem

Slide 66

Slide 66 text

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 66 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.

Slide 67

Slide 67 text

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 67 Instance A URI reference that identifies the specific occurrence of the problem

Slide 68

Slide 68 text

{ "type": "https://developer.nexmo.com/api- errors#unauthorized", "title": "Invalid credentials supplied", "detail": "You did not provide correct credentials.", "instance": "797a8f199c45014ab7b08bfe9cc1c12c" } RFC7807 68

Slide 69

Slide 69 text

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 69

Slide 70

Slide 70 text

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 70

Slide 71

Slide 71 text

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 71

Slide 72

Slide 72 text

401: No Credentials, Invalid Credentials 403: Feature disabled, Exceeded calls-per-second limit 422: Invalid product specified + more! Response Library 72

Slide 73

Slide 73 text

“ 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

Slide 74

Slide 74 text

6. Design First APIs Consistency through specifications 74

Slide 75

Slide 75 text

'400': $ref: '#/components/responses/ InvalidPayloadError' '401': $ref: '../shared_errors.yml#/components/ responses/BadCredentialsError' '405': $ref: '../shared_errors.yml#/components/ responses/InvalidRequestMethod' '406': $ref: '../shared_errors.yml#/components/ responses/InvalidAcceptHeader' Design First APIs 75

Slide 76

Slide 76 text

Design First APIs 76

Slide 77

Slide 77 text

Design First APIs 77

Slide 78

Slide 78 text

7. Collection Management Sorting, Filtering, Searching, Pagination 78

Slide 79

Slide 79 text

Sorting 79

Slide 80

Slide 80 text

GET /users? sort_by=email&order_by=asc Sorting 80

Slide 81

Slide 81 text

GET /users?sort_by=email.asc Sorting 81

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

Filtering 86

Slide 87

Slide 87 text

Filtering 87 Discrete data e.g. user role Continuous data e.g. subscription expiry time

Slide 88

Slide 88 text

Discrete Data 88 /users?role=admin /orders?shipped=true /calls?status=active

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

Continuous Data 92

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

Industry Standards 95

Slide 96

Slide 96 text

Industry Standards: SCIM 96 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

Slide 97

Slide 97 text

Searching 97

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

Pagination 99 Offset based Cursor based

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

Pagination: Cursor 101 GET /calls GET /calls? cursor=2018-01-19T12:33:51 GET /calls? cursor=2018-01-18T03:00:18

Slide 102

Slide 102 text

Pagination: Links 102 { "_links": { "next": { "href": "/calls?cursor=9274" } } } Link: ; rel="next",

Slide 103

Slide 103 text

8. Hypermedia Specifically, HAL+JSON 103

Slide 104

Slide 104 text

[ { "id": "78d335fa323d01149c3dd6f0d489", "name": "My Application", } ] GET /v2/applications/78d335fa323d01149c3dd6f0d489 104

Slide 105

Slide 105 text

[ { "id": "78d335fa323d01149c3dd6f0d48968cf", "name": "My Application", "_links": { "self": { "href": "/v2/applications/ 78d335fa323d01149c3dd6f0d48968cf" } } } ] GET /v2/applications/78d335fa323d01149c3dd6f0d489 105

Slide 106

Slide 106 text

[ { "id": "78d335fa323d01149c3dd6f0d48968cf", "name": "My Application", "_links": { "self": { "href": "/v2/applications/ 78d335fa323d01149c3dd6f0d48968cf" }, "numbers": { "href": "/v2/numbers? application=78d335fa323d01149c3dd6f0d48968cf" } } } ] GET /v2/applications/78d335fa323d01149c3dd6f0d489 106

Slide 107

Slide 107 text

"hints": { "allow": [ "GET", "PUT", "DELETE", "PATCH" ], "formats": { "application/json": {} } } Hints Format 107

Slide 108

Slide 108 text

{ "numbers": { "href": "/v2/numbers? application=78d335fa323d01149c3dd6f0d48968cf", "hints": { "allow": [ "GET", "PUT", "DELETE", "PATCH" ], "formats": { "application/json": {} } } } Hints Format 108

Slide 109

Slide 109 text

9. Rate Limiting Should we add rate limiting? 109

Slide 110

Slide 110 text

Yes. Rate Limiting 110

Slide 111

Slide 111 text

Options: • Per-server • Per-region • Per-user Rate Limiting 111

Slide 112

Slide 112 text

Refresh Rates: • Per-second limits • Interval • Bucket Rate Limiting 112

Slide 113

Slide 113 text

How about queueing? Rate Limiting 113

Slide 114

Slide 114 text

10. Documentation This isn't an optional task for your API 114

Slide 115

Slide 115 text

Open API Specification: Docs 115

Slide 116

Slide 116 text

Open API Specification: Postman 116

Slide 117

Slide 117 text

Open API Specification: Generated Code 117

Slide 118

Slide 118 text

Written Docs 118

Slide 119

Slide 119 text

Open API Specification: Generated Code 119

Slide 120

Slide 120 text

11. Conclusion What have we learned? 120

Slide 121

Slide 121 text