-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
executable file
·545 lines (481 loc) · 17.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Pragmatic RESTful API Design</title>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="css/reveal.min.css">
<link rel="stylesheet" href="css/theme/default.css" id="theme">
<!-- For syntax highlighting -->
<link rel="stylesheet" href="lib/css/zenburn.css">
<!-- If the query includes 'print-pdf', include the PDF print sheet -->
<script>
if( window.location.search.match( /print-pdf/gi ) ) {
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'css/print/pdf.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
}
</script>
<style>
.reveal table {
margin: auto;
border-collapse: collapse;
border-spacing: 0;
}
.reveal table th,
.reveal table td {
padding: 0.2em 0.5em 0.2em 0.5em;
border-bottom: 1px solid;
}
</style>
<!--[if lt IE 9]>
<script src="lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h1>Pragmatic RESTful API Design</h1>
<p>Chris Dail - @chrisdail</p>
<p>Director, Software Engineering at EMC</p>
<p><small>http://chrisdail.github.io/talk-pragmatic-rest-api-design/</small></p>
</section>
<section data-background="#651212">
<h2>Disclaimer</h2>
<ul>
<li>This is a pragmatic approach to REST (not a dogmatic one).</li>
<li>Ideas here are simply my opinion. I favour user experience and developer sanity over idealogical 'correctness'.</li>
<li>There is no REST spec or standard (but there is for HTTP)</li>
<li>Enterprise Software and Products vs SaaS</li>
</ul>
</section>
<section>
<img src="images/solve-all-the-things.jpg">
<p>We love to think our products will solve all customer problems but this is not realistic</p>
</section>
<section>
<h2>Why APIs?</h2>
<ul>
<li>Customer's don't want a product; they want a <strong>solution</strong> to their problem.</li>
<li>APIs allow integrating your product into the customer's solution.</li>
<li>Prevents the feeling of 'lock in'</li>
</ul>
</section>
<section>
<h2>v1.0 Feature</h2>
<ul>
<li>Most companies discover they need an API long after v1.0</li>
<li>Often an afterthough and maybe not given the attention it needs</li>
</ul>
</section>
<section>
<h2>Microservices</h2>
<ul>
<li>Distributed applications, Netflix</li>
<li>Applications build with small decoupled, self-contained components</li>
<li>Each component handles a single job</li>
<li>Components communicate with each other via well established APIs</li>
</ul>
</section>
<section>
<h2>What Kind of API do I need?</h2>
<img src="images/rest-area.png">
</section>
<section>
<h2>What is REST?</h2>
<p>Representational State Transfer</p>
<blockquote cite="http://en.wikipedia.org/wiki/Representational_state_transfer">
“HTTP based RESTful APIs are defined with these aspects: base URI, such as http://example.com/resources/ an Internet media type for the data. This is often JSON but can be any other valid Internet media type (e.g. XML, Atom, microformats, images, etc.) standard HTTP methods (e.g., GET, PUT, POST, or DELETE)” - Wikipedia
</blockquote>
</section>
<section>
<h2>Or simply...</h2>
<ul>
<li>Web APIs</li>
<li>Web Services</li>
<img class="fragment" src="images/no-soap.jpg">
</ul>
</section>
<section>
<h2>Why REST?</h2>
<ul>
<li>Universal language, the Web</li>
<li>Universally available protocol, HTTP</li>
<li>Name one programming language that cannot talk to an HTTP server</li>
</ul>
</section>
<section>
<h2>Elements to a Good API</h2>
<ul>
<li>Features (I'll assume you have this covered)</li>
<li>User Experience - Know your user</li>
<li>Great Documentation</li>
</ul>
</section>
<section>
<h1>User Experience</h1>
<img src="images/ux.jpg">
<ul class="fragment">
<li>External Consistency - Does it work like other APIs</li>
<li>Internal Consistency - Is the API consistent within itself</li>
</ul>
</section>
<section>
<h2>URLs</h2>
<ul>
<li>Resource Oriented</li>
<li>Nouns not Verbs</li>
<li>Pluralize Resources</li>
<li>Lower case, hypen separated</li>
<li>Short segments (1-2 words)</li>
<li>Top level component (when API gets big)</li>
</ul>
<pre><code>
/widgets
/storage-systems # Storage or Systems is not specific enough
/foo/things # 'foo' component allows multiple /things
</code></pre>
</section>
<section>
<h2>Query Parameters</h2>
<ul>
<li>Used for optional parameters, options, search, filter, paging</li>
<li>Should never be required - Sensible Defaults</li>
<li>Data format case</li>
<li>Short (1-2 words)</li>
</ul>
<pre><code>
/resources?type=volume
/widgets?limit=100
/foo/things?tenant=coke
/widgets?q=frank
</code></pre>
</section>
<section>
<h3 style="text-transform: none">CamelCase vs snake_case</h3>
<ul>
<li>vi vs emacs</li>
<li>Consistency!</li>
</ul>
</section>
<section>
<section>
<h2>Data Formats</h2>
<ul>
<li>JSON First, XML if required</li>
<li>Case Consistency with all formats</li>
<li>Keep fields short (1-3 words)</li>
<li>Handle Collections Differently</li>
</ul>
<pre><code>
"things: [
{ },
{ }
]
<things>
<thing></thing>
<thing></thing>
</things>
</code></pre>
</section>
<section>
<h2>Field Formatting</h2>
<ul>
<li>Numbers as <strong>numbers</strong>
<pre><code>
// This
"size_gb": 10
// Not this
"size": "10gb"
</code></pre>
</li>
<li>Dates ISO 8601 (xsd:dateTime, JavaScript standard) <br /><code>yyyy-mm-dd'T'hh[:mm]</code></li>
</ul>
</section>
</section>
<section>
<h2>Read vs Write data models</h2>
<ul>
<li>Command Query Responsibility Segregation (CQRS)</li>
<li>Separate models for read/write</li>
<li>Example: ID is required for update, but not create</li>
<li>Example: Password required on create, not part of read</li>
</ul>
</section>
<section>
<section data-markdown>
<script type="text/template">
## HTTP Methods
Create, Read, Update, Delete -> POST, GET, PUT and DELETE
| Operation | Summary
| ----------------------------- | -------------------------
| `POST /widgets` | Creates a new Widget
| `GET /widgets` | Lists widgets
| `GET /widgets/12` | Gets a Widget
| `PUT /widgets/12` | Updates a widget (whole resource)
| `DELETE /widgets/12` | Deletes a widget
</script>
</section>
<section data-markdown>
<script type="text/template">
## HTTP Methods <2>
| Operation | Summary
| ----------------------------- | -------------------------
| `PATCH /widgets/12` | Partial Update of resource
| `GET /widgets/12/knobs` | Retrieves sub-collection knobs
| `POST /widgets/12/knobs` | Updates sub-collection knobs
| `POST /widgets/12/action` | Performs an action on a resource (Verbs here)
</script>
</section>
</section>
<section>
<h2>Linking</h2>
<ul>
<li>Self-describing relationships and capabilities</li>
<li>HATEOAS - Links as part of data</li>
<li>Link Headers</li>
</ul>
<pre><code>
GET /widgets/12
Link: <https://host/v1/widgets/12/knobs>; rel="knobs"
</code></pre>
</section>
<section>
<section data-markdown>
<script type="text/template">
## Paging
- You can't always return everything (Scalability)
- Offset or Marker based
- Include links for next/previous/last/first
| Query Parameter | Example (GET)
| --------------------------------- | -------------------------
| **limit**, count, max | `/widgets?limit=100`
| **offset**, index, page | `/widgets?limit=100&offset=100`
| **marker**, since_id, max_id | `/widgets?marker=id123`
</script>
</section>
<section>
<h2>Paging - Example</h2>
<pre><code>
GET /widgets?limit=100
Link: </widgets?limit=100&offset=100>; rel="next",
</widgets?limit=100&offset=22300>; rel="last"
GET /widgets?limit=100
Link: </widgets?limit=100&marker=id123>; rel="next"
</code></pre>
</section>
</section>
<section data-markdown>
<script type="text/template">
## Sorting, Filtering and Searching
Rarely do users want everything
| Query | Example (GET)
| --------------- | -------------------------
| sort | `/widgets?sort=name`
| | `/widgets?sort=owner,-last_modified`
| q | `/widgets?q=frank&sort=owner`
| - | `/widgets?type=floodle`
| | `/widgets?state=pending&tenant=coke`
| fields | `/widgets?fields=name,state,type,id`
</script>
</section>
<section>
<section data-markdown>
<script type="text/template">
## Error Codes
| Code | Description
| --------------------------- | -------------------------
| `200 OK` | Request processed as expected
| `201 Created` | Request created a resource, Set Location Header
| `202 Accepted` | Request accepted and pending (Asynchronous), Set Location Header
| `204 No Content` | Same as 200 except no content returned
| `302 Moved Temporarily` | Standard redirect, Set Location Header
| `304 Not Modified` | Cache still good. User requests If-None-Match or If-Modified-Since
</script>
</section>
<section data-markdown>
<script type="text/template">
## Error Codes <2>
| Code | Description
| --------------------------- | -------------------------
| `400 Bad Request` | Client submitted bad request (Validation)
| `401 Unauthorized` | Not authenticated or token expired
| `403 Forbidden` | User does not have permission
| `404 Not Found` | Resource not found for ID
| `500 Internal Server Error` | Server error, should not be retried
| `503 Service Unavailable` | Retriable error, typically for connection issues
</script>
</section>
</section>
<section>
<section>
<h1>Versioning</h1>
<ul>
<li>Content Negotiation<br />
<code>Accepts: application/vnd.widgets+json; version="1"</code></li>
<li>URL Based<br />
<code>https://hostname/v1/widgets/12</code></li>
</ul>
</section>
<section>
<h2>URL Based</h2>
<ul>
<li>Easiest to implement for you</li>
<li>Easiest to implement for users (no headers)</li>
<li>Most widely used in public APIs</li>
</ul>
</section>
<section>
<h2>Backwards Compatibility</h2>
<img src="images/backwards-compatible.jpg">
</section>
<section>
<h2>API Change Types</h2>
<ul>
<li>
Backwards Compatible
<ul>
<li>Additions only. Never remove or rename.</li>
<li>New fields added to model must be optional</li>
</ul>
</li>
<li>
Backwards Incompatible
<ul>
<li>Renaming or moving API roots or fields</li>
<li>Deleting APIs</li>
<li>Adding Required Fields</li>
<li>Changes to authentication or headers/cookies</li>
<li>Changes to behaviour</li>
</ul>
</li>
</ul>
</section>
<section>
<h2>Versioning Rules</h2>
<ul>
<li>API version should be a positive integer. Ex: v1, v2, v3</li>
<li>Not every product release needs an API version</li>
<li>New version only when <strong>backwards incompatible change</strong> required</li>
<li>It is easiest to add versioning concepts in v1!</li>
</ul>
</section>
</section>
<section>
<section>
<h2>Common Data Formats</h2>
<ul>
<li>Errors</li>
<li>Async Tasks</li>
<li>Links</li>
</ul>
</section>
<section>
<h2>Error Format</h2>
<pre><code>
POST /widgets/12
{
"code": 9001,
"message": "Error updating resource",
"details": "Error updating resource with ID 12 because of bla"
}
</code></pre>
</section>
<section>
<h2>Error Format (Validation)</h2>
<pre><code>
POST /widgets/12
{
"code": "9001",
"message": "Validation Error",
"details": "Validation error with the hostname field"
"fields": [
{
"name": "hostname",
"code": "2001",
"message": "Hostname field not specified but is required"
}
]
}
</code></pre>
</section>
</section>
<section>
<h2>Authentication</h2>
<ul>
<li>Always use SSL</li>
<li>Public APIs, use standard SSO like OAuth</li>
<li>HTTP Basic Auth, Auth Token in cookies/headers</li>
<li>Browser Explorable</li>
</ul>
</section>
<section>
<h2>Caching</h2>
<ul>
<li>ETag - Hash/sum in <code>ETag</code> header. <code>If-None-Match</code></li>
<li>Last-Modified - <code>Last-Modified</code> header. <code>If-Modified-Since</code></li>
</ul>
</section>
<section>
<h2>Random Thoughts</h2>
<ul>
<li>Consolidate APIs under single domain</li>
<li>Support GZip encoding</li>
<li>Consider an SDK for target developers</li>
</ul>
</section>
<section>
<h2>Documentation</h2>
<ul>
<li>Github and Stackoverflow good examples</li>
<li>Consider generating documentation</li>
<li>Inline testing</li>
</ul>
</section>
<section data-markdown>
<script type="text/template">
## Swagger
- API descriptor file
- Online Editor http://editor.swagger.io
- Interactive Documentation http://petstore.swagger.wordnik.com/
- Code Generation (Client and Server)
- Alternatives: I/O Docs, Carte
</script>
</section>
<section>
<h2>Game</h2>
<ul>
<li>What's Wrong With This API</li>
<li>Name some other poor APIs</li>
</ul>
</section>
</div>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.min.js"></script>
<script>
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
theme: 'moon',
transition: 'linear',
dependencies: [
{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
{ src: 'plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
]
});
</script>
</body>
</html>