-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathindex.html
675 lines (648 loc) · 25.4 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
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
<!DOCTYPE html>
<html lang="en">
<head>
<title>JSON-LD Best Practices</title>
<meta charset='utf-8'>
<script src="https://www.w3.org/Tools/respec/respec-w3c" class="remove" defer></script>
<script type="text/javascript" src="common/common.js" class="remove" defer></script>
<script type="text/javascript" src="common/jsonld.js" class="remove"></script>
<script class='remove'>
var respecConfig = {
localBiblio: {
...jsonld.localBiblio,
...{
"swagger": {
"title": "Swagger: API Development for Everyone",
"href": "https://swagger.io"
},
"json-ld-best-practice-caching": {
"authors": [
"Manu Sporny",
"David Longley"
],
"title": "JSON-LD Best Practice: Context Caching",
"href": "http://manu.sporny.org/2016/json-ld-context-caching/",
"date": "2016-04-24"
}
}
},
specStatus: "DNOTE",
copyrightStart: "2019",
shortName: "json-ld-bp",
edDraftURI: "https://w3c.github.io/json-ld-bp/",
github: {
repoURL: "https://github.com/w3c/json-ld-bp/",
branch: "main"
},
editors: [{
name: "Gregg Kellogg",
url: "http://greggkellogg.net/",
w3cid: "44770"
}],
//previousMaturity: "NOTE",
//previousPublishDate: "1977-03-15",
github: {
repoURL: "https://github.com/w3c/json-ld-bp/",
branch: "main"
},
doJsonLd: true,
group: "wg/json-ld",
wgPublicList: "public-json-ld-wg",
maxTocLevel: 4,
noRecTrack: true
};
</script>
<style>
.hl-bold { font-weight: bold; color: #0a3; }
.comment { color: #999; }
</style>
</head>
<body>
<section id='abstract'>
<p>
Developers share a common problem: they want a simple,
but extensible way to create an API for a web service that gets the job done,
doesn't design them into a corner,
and allows developers to easily interact with their service without reinventing the wheel.
JSON-LD [[JSON-LD]] has become an important solution,
as it bridges the gap between formally data
and more colloquial JSON interfaces used in APIs from numerous providers.
This guide attempts to define certain best practices for publishing data using JSON-LD,
and interacting with such services.
</p>
</section>
<section id='sotd'>
<p>This unofficial document has been developed by the
<a href="https://www.w3.org/2018/json-ld-wg/">JSON-LD Working Group</a>.</p>
</section>
<section id="conformance">
<p>This document describes best practices for generating JSON-LD.
Where normative language is used, it should be considered advisory.
</p>
</section>
<section class="informative">
<h2>Introduction</h2>
<p>
Coming up with a data format for your API is a common problem.
It can be hard to choose between different data representations,
what names you want to pick,
and even harder if you want to leave room for extensibility.
How do you make all these decisions?
How do you make your API easy to use so people can use short strings to reference common things,
but URLs to enable people to come up with their own so it isn't limiting?
How can you make it easy for other people to add their own data in and make it interoperable?
How do you consume data from other similar apps?
There are technologies that can help you do this.
Now, it isn't perfect – sometimes it won't solve your problem,
but it could maybe solve a lot of them.
</p>
<p>
The use of JSON on the web has grown immensely in the last decade,
particularly with the explosion of APIs that eschew XML
in favor of what is considered to be a more developer friendly format
which is directly compatible with JavaScript.
As a result, different sites have chosen their own
proprietary representations for interacting with their sites,
sometimes described using frameworks such as [[swagger]]
which imply a particular URI composition for interacting with their services.
This practice leads to vendor-specific semantic silos,
where the meaning of a particular JSON document makes sense
only by programming directly to the API documentation for a given service.
</p>
<p class="ednote">
show examples from GitHub, Twitter, …?
</p>
<p>
As services grow they often introduce incompatible changes leading
to a Version 2 or Version 3 of their API
requiring developers to update client code
to properly handle JSON documents.
In many cases, even small changes can lead to incompatibilities.
Additionally, composing information from multiple APIs becomes problematic,
due to namespace or document format conventions that may differ between API endpoints.
Moreover, the same principles are often repeated across different endpoints
using arbitrary identifiers (name, email, website, etc.);
the community needs to learn to stop repeating itself
(<abbr title="Don't Repeat Yourself">DRY</abbr> concept)
and reuse common conventions,
although this does not necessarily have to mean using exactly
the same identifiers within the JSON itself (see <a>JSON-LD Context</a>).
</p>
<p>
This Note proposes to outline a number of best practices
for API designers or JSON developers
based on the principles of separation of data model from syntax,
the use of discoverable identifiers describing document contents,
and general organizing principles that allow documents
to be machine understandable
(read, interpreted as <a>JSON-LD</a> using <a>Linked Data</a>,
RDF and RDFS vocabulary, and data model principles).
</p>
<p>
Key among these is the notion of vocabulary re-use,
so that each endpoint does not need to separately describe
the properties and structure of their JSON documents.
Schema.org provides a great example of doing this,
and includes an extension mechanism
that may already be familiar to API designers.
</p>
<p>
<a>JSON-LD</a> is JSON,
and good JSON-LD is first and foremost good JSON.
Since it is also <a>Linked Data</a>,
developers and especially data publishers may find
further useful advice at <cite><a href="http://www.w3.org/TR/dwbp/">Data on the Web Best Practices</a></cite> [[dwbp]]
and <cite><a href="http://www.w3.org/TR/ld-bp/">Best Practices for Publishing Linked Data</a></cite> [[ld-bp]].
</p>
</section>
<section id='bp-summary'></section>
<section class="informative">
<h2>Terminology</h2>
<dl class="termlist">
<dt><dfn>JSON-LD</dfn></dt>
<dd>
JSON-LD [[JSON-LD]] is a lightweight <a>Linked Data</a> format.
It is easy for humans to read and write.
It is based on the already successful JSON format
and provides a way to help JSON data interoperate at Web-scale.
JSON-LD is an ideal data format for programming environments,
REST Web services, and unstructured databases
such as CouchDB and MongoDB.
</dd>
<dt><dfn>JSON-LD Context</dfn></dt>
<dd>
In <a>JSON-LD</a>,
a <a data-cite="JSON-LD11#dfn-context">context</a>
is used to map terms,
i.e., properties with associated values in an JSON document, to URLs.
A <a data-cite="JSON-LD11#dfn-term">term</a> is a short word
that expands to a URL.
Terms may be generally defined as any valid JSON string
other than a JSON-LD keyword.
</dd>
<dt><dfn>Linked Data</dfn></dt>
<dd>
Linked Data [[linked-data]] is a way to create
a network of standards-based machine interpretable data
across different documents and Web sites.
It allows an application to start at one piece of Linked Data,
and follow embedded links to other pieces of Linked Data
that are hosted on different sites across the Web.
</dd>
</dl>
</section>
<section class="informative">
<h2>Data Documents</h2>
<div class="practice">
<p class="practicedesc">
<span id="use-json" class="practicelab">Publish data using developer friendly JSON</span>
JSON [[json]] is the most popular format for publishing data through APIs;
developers like it,
it is easy to parse,
and it is supported natively in most programming languages.
</p>
</div>
<p>For example, the following is reasonably idiomatic JSON which can also be interpreted as JSON-LD, given the appropriate context.</p>
<pre class="example"
data-transform="updateExample"
title="Example JSON representing a Person">
{
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States"
}
</pre>
<div class="practice">
<p class="practicedesc">
<span id="top-level-object" class="practicelab">Use a top-level object</span>
JSON documents may be in the form of a object,
or an array of objects.
For most purposes, developers need a single entry point,
so the JSON SHOULD be in the form of a single top-level object.
</p>
</div>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="native-values">Use native values</span>
When possible, property values SHOULD use native JSON datatypes
such as numbers (<em>integer</em>, <em>decimal</em> and <em>floating point</em>)
and booleans (`true` and `false`).
</p>
</div>
<p class="note">JSON has a single numeric type,
so using native representation of numbers can lose precision.
</p>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="unordered-values">Assume arrays are unordered</span>
JSON specifies that the values in an array are ordered,
however in many cases arrays are also used for values which are unordered.
Unless specified within the <a>JSON-LD Context</a>,
multiple array values SHOULD be presumed to be unordered. (See <cite><a href="http://www.w3.org/TR/json-ld/#lists-and-sets">Lists and Sets</a></cite> in [[JSON-LD]]).</p>
</div>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="use-json-ld">Use well-known identifiers when describing data</span>
By sticking to basic JSON data expression,
and providing a <a>JSON-LD Context</a>,
all keys used within a JSON document can have unambiguous meaning,
as they bind to URLs which describe their meaning.
</p>
</div>
<p>By adding an `@context` entry,
the previous example can now be interpreted as JSON-LD.</p>
<pre class="example"
data-transform="updateExample"
title="Example JSON-LD identifying a person">
{
****"@context": "http://schema.org"****,
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States"
}
</pre>
<div class="note">
<p>When expanding such a data representation,
a JSON-LD processor replaces these terms with the URIs they expand to
(as well as making property values unambiguous):
</p>
<pre class="example"
data-transform="updateExample"
title="Example JSON-LD identifying a person (expanded)">
[
{
"http://schema.org/familyName": [{"@value": "Obama"}],
"http://schema.org/givenName": [{"@value": "Barack"}],
"http://schema.org/jobTitle": [{"@value": "44th President of the United States"}],
"http://schema.org/name": [{"@value": "Barack Obama"}]
}
]
</pre>
<p>Expanded form is not useful as is,
but is necessary for performing further algorithmic transformations
of JSON-LD data and is useful when validating
that JSON-LD entity descriptions say what the publisher means.
</p>
</div>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="typed-objects">Provide one or more types for JSON objects</span>
Principles of <a>Linked Data</a> dictate that
messages SHOULD be self describing,
which includes adding a `type` to such messages.</p>
</div>
<p>Many APIs use JSON messages
where the type of information being conveyed is inferred from the retrieval endpoint.
For example, when retrieving information about a Github Commit,
you might see the following response:</p>
<pre class="example"
data-transform="updateExample"
title="Github Commit message">
{
"sha": "7638417db6d59f3c431d3e1f261cc637155684cd",
"url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd",
"author": {
"date": "2014-11-07T22:01:45Z",
"name": "Scott Chacon",
"email": "schacon@gmail.com"
},
"committer": {
"date": "2014-11-07T22:01:45Z",
"name": "Scott Chacon",
"email": "schacon@gmail.com"
},
"message": "added readme, because im a good github citizen\n",
"tree": {
"url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb",
"sha": "691272480426f78a0138979dd3ce63b77f706feb"
},
"parents": [
{
"url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5",
"sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5"
}
]
}
</pre>
<p>The only way to know this is a commit
s to infer it based on the published API documentation,
and the fact that it was returned from an endpoint
defined for retrieving information about commits.</p>
<pre class="example"
data-transform="updateExample"
title="Example message of type Person">
{
"@context": "http://schema.org",
"id": "http://www.wikidata.org/entity/Q76",
****"type": "Person"****,
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States"
}
</pre>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="use-ids">Identify objects with a unique identifier</span>
Entities described in JSON objects often describe web resources having a URL;
entity descriptions SHOULD use an identifier uniquely identifying that entity.
In this case, using the resource location as the identity of the object
is consistent with this practice.</p>
</div>
<p>Adding an `id` entry (an alias for `@id`) allows the same person
to be referred to from different locations.</p>
<pre class="example"
data-transform="updateExample"
title="Example JSON-LD identifying a person">
{
"@context": "http://schema.org",
****"id": "http://www.wikidata.org/entity/Q76"****,
"type": "Person",
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States"
}
</pre>
<p class="note">There can be ambiguity if an identifier describes the entity description,
or directly represents that entity itself.
As an example, Barack Obama may have a Wikidata entry `http://www.wikidata.org/entity/Q76`,
but it would be a mistake to say that `http://www.wikidata.org/entity/Q76` <em>is</em> Barack Obama.
However, it is common to use this pattern,
particularly if the <strong>type</strong> of the entity describes a Person,
rather than a WebPage.</p>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="things-not-strings">Things not strings</span>
When describing attributes,
entity references SHOULD be used instead of string literals.</p>
</div>
<p>In some cases, when describing an attribute of an entity,
it is tempting to using string values which have no independent meaning.
Such values are often used for well known things.
A JSON-LD context can define a term for such values,
which allow them to appear as strings within the message,
but be associated with specific identifiers.
In this case, the property must be defined with type `@vocab`
so that values will be interpreted relative to a vocabulary
rather than the file location.</p>
<pre class="example"
data-transform="updateExample"
title="Example enumerated value">
{
"@context": ["http://schema.org", ****{
"gender": {"@id": "schema:gender", "@type": "@vocab"}
}****],
"id": "http://www.wikidata.org/entity/Q76",
"type": "Person",
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States",
****"gender": "Male"****
}
</pre>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="embed-objects">Nest referenced inline objects</span>
When multiple related entity descriptions are provided inline,
related entities SHOULD be nested.</p>
</div>
<p>For example, when relating one entity to another,
where the related entity is described in the same message:</p>
<pre class="example"
data-transform="updateExample"
title="Nested relationships">
{
"@context": "http://schema.org",
"id": "http://www.wikidata.org/entity/Q76",
"type": "Person",
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States",
****"spouse": {
"id": "http://www.wikidata.org/entity/Q13133",
"type": "Person",
"name": "Michelle Obama",
"spouse": "http://www.wikidata.org/entity/Q76"
}****
}
</pre>
<p class="note">In this example, the `spouse` relationship is bi-directional,
we have arbitrarily rooted the message with Barack Obama,
and created a symmetric relationship
from Michelle back to Barack by reference,
rather than by nesting.</p>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="reverse-references">When describing an inverse relationship, use a referenced property</span>
FIXME
</p>
</div>
</section>
<section class="informative">
<h2>Contexts</h2>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="type-links">External references SHOULD use typed term</span>
When using a property intended to reference another entity,
properties SHOULD be defined to type string values as being references.</p>
</div>
<p>For example, the `schema:image` property a `Thing` to an `Image`:</p>
<pre class="example"
data-transform="updateExample"
title="Example typed relationship">
{
"@context": "http://schema.org",
"id": "http://www.wikidata.org/entity/Q76",
"type": "Person",
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States",
****"image": "https://commons.wikimedia.org/wiki/File:President_Barack_Obama.jpg"****
}
</pre>
<p>This will be interpreted as a reference,
rather than a string literal,
because (at the time of publication),
the schema.org JSON-LD Context defines `image` to be of type `@id`:</p>
<pre class="example"
data-transform="updateExample"
title="schema.org context snippet for image">
{
"@context": {
####...####
"image": { "@id": "schema:image", ****"@type": "@id"****}####,
...####
}
}
</pre>
<p>If not defined as such in a remote context,
terms may be (re-) defined in a local context:</p>
<pre class="example"
data-transform="updateExample"
title="Example typed relationship">
{
"@context": ["http://schema.org", ****{
"image": { "@id": "schema:image", "@type": "@id"}
}****],
"id": "http://www.wikidata.org/entity/Q76",
"type": "Person",
"name": "Barack Obama",
"givenName": "Barack",
"familyName": "Obama",
"jobTitle": "44th President of the United States",
"image": "https://commons.wikimedia.org/wiki/File:President_Barack_Obama.jpg"
}
</pre>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="context-order">Ordering of array elements</span>
Unless specifically described ordered as an `@list`,
do not depend on the order of elements in an array.</p>
</div>
<p>By default,
<a data-cite="JSON-LD11#sets-and-lists">
arrays in JSON-LD do not convey any ordering of contained elements
</a>.
However, for the processing of contexts,
the ordering of elements in arrays <em>does</em> matter.
When writing array-based contexts, this fact should be kept in mind.</p>
<p>Ordered contexts in arrays allow inheritance
and overriding of context entries.
When processing the following example,
the first `name` entry will be overridden by the second `name` entry.</p>
<pre class="example"
data-transform="updateExample"
title="Overriding name term">
{
"@context": [
{
"id": "@id",
"name": "http://schema.org/name"
},
{
"name": "http://xmlns.com/foaf/0.1/name"
}
],
"@id": "http://www.wikidata.org/entity/Q76",
****"name": "Barack Obama"****
}
</pre>
<p>Order is important when processing
<a data-cite="JSON-LD11#protected-term-definitions">protected terms</a>.
While the first example will cause a term redefinition error,
the second example will not throw this error.</p>
<pre class="illegal-example"
data-transform="updateExample"
title="Failing term redefinition">
{
"@context": [
{
"@version": 1.1,
"name": {
"@id": "http://schema.org/name",
"@protected": true
}
},
{
"name": "http://xmlns.com/foaf/0.1/name"
}
],
"@id": "http://www.wikidata.org/entity/Q76",
****"name": "Barack Obama"****
}
</pre>
<pre class="example"
data-transform="updateExample"
title="Non-failing term redefinition">
{
"@context": [
{
"name": "http://xmlns.com/foaf/0.1/name"
},
{
"@version": 1.1,
"Person": "http://schema.org/Person",
"knows": "http://schema.org/knows",
"name": {
"@id": "http://schema.org/name",
"@protected": true
}
}
],
"@id": "http://www.wikidata.org/entity/Q76",
****"name": "Barack Obama"****
}
</pre>
</section>
<section class="informative">
<h2>Publishing</h2>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="dereferencable-entities">Provide a representation of the entity related by URL</span>
When dereferencing an entity related via a URL,
the location SHOULD provide a representation of that entity.</p>
</div>
<p class="note">This practices replicates that described in [[ld-bp]]
<a data-cite="ld-bp#uri-design-principles">
Provide at least one machine-readable representation
of the resource identified by the URI
</a>.</p>
<p>Corollaries to this best practice is that
Cool URIs don't change [[cooluris]],
meaning that URLs describing entities SHOULD be stable
and not depend on variable information.
Also, the URL used to identify an entity is the best API endpoint of that entity
(see also <a href="#api-versioning" class="sectionRef"></a>).</p>
</section>
<section class="informative">
<h2>Consuming</h2>
<p>While most use of JSON-LD SHOULD NOT require a client
to change the data representation,
JSON-LD does allow the use of various algorithms
to re-shape a JSON-LD document.
These require the use of the <a>JSON-LD Context</a>,
which is typically represented using a link to a remote document.
Because it is remote,
processing time can be severely impacted
by the time it takes to retrieve this context.</p>
<div class="practice">
<p class="practicedesc">
<span class="practicelab" id="cache-context">Cache JSON-LD Contexts</span>
Services providing a <a>JSON-LD Context</a> SHOULD
set HTTP cache-control headers to allow liberal caching of such contexts,
and clients SHOULD attempt to use a locally cached version
of these documents.</p>
</div>
<p>Typically, libraries used to process JSON-LD documents
should do this for you.
(See also [[json-ld-best-practice-caching]]).</p>
</section>
<section class="informative">
<h2>Serializing Large Collections</h2>
<p class="ednote">Describe schema.org extension using Role sub-class,
Hydra collections, and LDP collections.</p>
</section>
<section class="informative">
<h2>Reuse Vocabularies</h2>
<p class="ednote">Focus on schema.org?</p>
</section>
<section class="informative">
<h2>Describe API affordances</h2>
<p class="ednote">Describe the use of schema.org Actions and work in Hydra.</p>
<p class="ednote">Describe anti-pattern of URI construction emphasizing affordances.</p>
</section>
<section class="informative">
<h2>API Versioning</h2>
<p class="ednote">Remember that Cool URIs don't change [cooluris];
correctly modeling data allows changes data representation to be limited.</p>
<p class="ednote">Describe the use of API keys for controlling API versions,
rather than the use of different versioned URLs.</p>
</section>
</body>
</html>