Skip to content

Commit 2eb3ba9

Browse files
v1 (#1)
* Update heading * Add description * Add notational conventions * Add entity typing BP * Add primitive props BP * Add relation types BP * Add entity link type hint BP * Add non-entity link BP * Add self link BP * Add profile link BP * Add relative URI resolution BP * Add documentation appendix * Add TOC * Remove notational conventions
1 parent ac0e59f commit 2eb3ba9

File tree

1 file changed

+214
-1
lines changed

1 file changed

+214
-1
lines changed

README.md

Lines changed: 214 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,214 @@
1-
# best-practices
1+
# Siren Best Practices
2+
3+
This document outlines best practices for Siren API servers and clients. It does not intend to alter or usurp [the original specification](https://github.com/kevinswiber/siren).
4+
5+
## Table of Contents <!-- omit in toc -->
6+
7+
- [Specify Entity's Type](#specify-entitys-type)
8+
- [Prefer Primitive Properties](#prefer-primitive-properties)
9+
- [Follow Relation Type Standards](#follow-relation-type-standards)
10+
- [Provide Type Hint to Entity Links](#provide-type-hint-to-entity-links)
11+
- [Distinguish Links to Non-Siren Resources](#distinguish-links-to-non-siren-resources)
12+
- [Include `self` Links](#include-self-links)
13+
- [Link to API Documentation](#link-to-api-documentation)
14+
- [Resolve Relative URIs](#resolve-relative-uris)
15+
- [Appendix A: API Documentation](#appendix-a-api-documentation)
16+
17+
[embedded-link]: https://github.com/kevinswiber/siren#embedded-link
18+
[link]: https://github.com/kevinswiber/siren#links-1
19+
[sub-entity]: https://github.com/kevinswiber/siren#sub-entities
20+
21+
## Specify Entity's Type
22+
23+
As the server, include one or more type identifiers in the entity `class` array. A type identifier indicates which set of `properties`, action `name`s, and [link][link] and [sub-entity][sub-entity] relation types (`rel`) might be present in the entity.
24+
25+
Type identifiers can be anything from a simple string to a fully-qualified class name (e.g., `order`, `com.example.Order`). Be consistent in type identifier naming conventions, and [document them](#appendix-a-document-your-api).
26+
27+
```json
28+
{
29+
"class": ["Person"],
30+
"links": [
31+
{
32+
"rel": ["profile"],
33+
"href": "https://schema.org/Person"
34+
},
35+
{
36+
"rel": ["https://schema.org/knows"],
37+
"href": "https://example.com/hermione",
38+
"class": ["Person"]
39+
}
40+
],
41+
"properties": {
42+
"givenName": "Neville",
43+
"familyName": "Longbottom",
44+
"birthDate": "1980-07-30"
45+
}
46+
}
47+
```
48+
49+
## Prefer Primitive Properties
50+
51+
Prefer primitives (string, number, boolean, or null) or arrays of primitives as the values of an entity's `properties` object. An object or object array property value is typically a sign that another entity/resource is more appropriate and should be hyperlinked in the context entity.
52+
53+
## Follow Relation Type Standards
54+
55+
[Links][link]' and [embedded links][embedded-link]' relation types conform to [Section 3.3 of RFC 8288](https://www.rfc-editor.org/rfc/rfc8288#section-3.3), which obsoletes [the RFC mentioned in the spec](https://www.rfc-editor.org/rfc/rfc5988). Thus `rel` values are either a name from [the IANA link relations registry](https://www.iana.org/assignments/link-relations/link-relations.xhtml), a name in [your API documentation](#appendix-a-document-your-api), or an absolute URI. When using the latter, use a URL that points to documentation describing the link relation type.
56+
57+
```json
58+
{
59+
"links": [
60+
{
61+
"rel": ["self"],
62+
"href": "/orders/69"
63+
},
64+
{
65+
"rel": ["https://schema.org/customer"],
66+
"href": "/people/42"
67+
}
68+
],
69+
"entities": [
70+
{
71+
"rel": ["https://api.example.com/about#order-items"],
72+
"href": "/orders/69/items"
73+
}
74+
]
75+
}
76+
```
77+
78+
## Provide Type Hint to Entity Links
79+
80+
When a [link][link] or [embedded link][embedded-link] reference another Siren entity, match the link's `class` property with the target entity's `class` property.
81+
82+
```json
83+
{
84+
"rel": ["author"],
85+
"href": "/people/42",
86+
"class": ["Person"]
87+
}
88+
```
89+
90+
```http
91+
GET /people/42 HTTP/1.1
92+
Host: api.example.com
93+
```
94+
95+
```http
96+
HTTP/1.1 200 OK
97+
Content-Type: application/vnd.siren+json
98+
99+
{
100+
"class": ["Person"],
101+
"links": [
102+
{ "rel": ["self"], "href": "/people/42" }
103+
]
104+
}
105+
```
106+
107+
## Distinguish Links to Non-Siren Resources
108+
109+
When a [link][link] points to a non-Siren resource, specify the target's default media type in the `type` property.
110+
111+
```json
112+
{
113+
"rel": ["icon"],
114+
"href": "https://api.example.com/images/icon",
115+
"type": "image/png"
116+
}
117+
```
118+
119+
> Note that `type` is only a hint; for example, it does not override the `Content-Type` header field of a HTTP response obtained by actually following the link. [[RFC 8288, Section 3.4.1](https://www.rfc-editor.org/rfc/rfc8288#section-3.4.1)]
120+
121+
## Include `self` Links
122+
123+
Include a `self` [link][link] for every entity. An entity represents a resource, which is identified by a URL, and therefore has a `self` link.
124+
125+
```json
126+
{
127+
"rel": ["self"],
128+
"href": "/orders/21"
129+
}
130+
```
131+
132+
## Link to API Documentation
133+
134+
Include a `profile` link [[RFC6906](https://www.rfc-editor.org/rfc/rfc6906.html)] for every entity. The [link][link] points to relevant [documentation describing the semantics of the resource](#appendix-a-document-your-api).
135+
136+
```json
137+
{
138+
"rel": ["profile"],
139+
"href": "https://api.example.com/about#Person",
140+
"type": "application/xhtml+xml"
141+
}
142+
```
143+
144+
## Resolve Relative URIs
145+
146+
When a [link][link]'s or [embedded link][embedded-link]'s `href` is a [relative URI](https://www.rfc-editor.org/rfc/rfc3986#section-4.2), [resolve the reference](https://www.rfc-editor.org/rfc/rfc3986#section-5) by [establishing a base URI](https://www.rfc-editor.org/rfc/rfc3986#section-5.1) as follows:
147+
148+
1. [Base URI Embedded in Content](https://www.rfc-editor.org/rfc/rfc3986#section-5.1.1). This is the context entity's `self` link's `href`.
149+
2. [Base URI from the Encapsulating Entity](https://www.rfc-editor.org/rfc/rfc3986#section-5.1.2). If the context entity has no `self` link with an absolute URI and it is a [sub-entity](https://github.com/kevinswiber/siren#sub-entities), then traverse the entity graph up from the context, searching for a `self` link with an absolute URI at each level, and stopping when one is found or the graph is exhausted. The `href` of that link, if found, is the base URI.
150+
3. [Base URI from the Retrieval URI](https://www.rfc-editor.org/rfc/rfc3986#section-5.1.3). If no `self` link is found in the entity graph, then the URI used to retrieve the entity is the base URI.
151+
152+
Let's work through an example. Consider the following `Order` entity:
153+
154+
```json
155+
{
156+
"class": ["Order"],
157+
"links": [
158+
{
159+
"rel": ["self"],
160+
"href": "https://api.example.com/orders/69"
161+
}
162+
],
163+
"entities": [
164+
{
165+
"rel": ["https://schema.org/orderedItem"],
166+
"class": ["OrderItem"],
167+
"links": [
168+
{
169+
"rel": ["self"],
170+
"href": "https://api.eg.io/orders/69/items/1"
171+
}
172+
],
173+
"entities": [
174+
{
175+
"rel": ["https://schema.org/seller"],
176+
"class": ["Organization"],
177+
"links": [
178+
{
179+
"rel": ["self"],
180+
"href": "https://example.org"
181+
}
182+
],
183+
"entities": [
184+
{
185+
"rel": ["https://schema.org/member"],
186+
"class": ["Person"],
187+
"href": "/people/42"
188+
}
189+
]
190+
}
191+
]
192+
}
193+
]
194+
}
195+
```
196+
197+
In order to resolve the `https://schema.org/member` `Person` sub-entity, we need to first resolve its `href`. We start by checking the context `Organization` entity for a `self` link. Since one is present, we use it's `href` as the base URI, making the `Person` sub-entity's URI `https://example.org/people/42`.
198+
199+
If however the `Organization` entity had no `self` link or it was itself relative, we would move up the entity graph once and check again for a `self` link. In that case, we would use the `OrderItem` entity's `self` link's `href` as the base URI, and the `Person` sub-entity's URI then becomes `https://api.eg.io/people/42`.
200+
201+
Similarly, had the `OrderItem` entity's `self` link been missing, we would once again traverse and search the entity graph. This time we fine the `Order` entity's `self` link and thus our `Person` sub-entity's URI is `https://api.example.com/people/42`.
202+
203+
Finally, if no `self` links had been present, the URI used to retrieve the entity would be the base URI since we have exhausted the entity graph.
204+
205+
## Appendix A: API Documentation
206+
207+
At minimum, Siren API documentation describes the following:
208+
209+
- `class` values
210+
- Names of `properties`
211+
- `name`s of `actions` and `fields`
212+
- Custom `rel` values
213+
214+
The exact format of Siren API documentation is beyond the scope of this document. Consider using a format designed for documenting APIs like [ALPS](http://alps.io/) or [XMDP](https://gmpg.org/xmdp/).

0 commit comments

Comments
 (0)