22 |
23 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/hal.css:
--------------------------------------------------------------------------------
1 | html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
2 |
3 | body{
4 | color:#444;
5 | font-family: Helvetica, sans-serif;
6 | font-size:12px;
7 | line-height:1.5em;
8 | padding:1em;
9 | margin:auto;
10 | max-width:960px;
11 | width: 960px;
12 | background:#fefefe;
13 | }
14 |
15 | a{ color: #0645ad; text-decoration:none;}
16 | a:visited{ color: #0b0080; }
17 | a:hover{ color: #06e; }
18 | a:active{ color:#faa700; }
19 | a:focus{ outline: thin dotted; }
20 | a:hover, a:active{ outline: 0; }
21 |
22 | ::-moz-selection{background:rgba(255,255,0,0.3);color:#000}
23 | ::selection{background:rgba(255,255,0,0.3);color:#000}
24 |
25 | a::-moz-selection{background:rgba(255,255,0,0.3);color:#0645ad}
26 | a::selection{background:rgba(255,255,0,0.3);color:#0645ad}
27 |
28 | p{
29 | margin:1em 0;
30 | }
31 |
32 | img{
33 | max-width:100%;
34 | }
35 |
36 | h1,h2,h3,h4,h5,h6{
37 | font-weight:normal;
38 | color:#111;
39 | line-height:1em;
40 | }
41 | h4,h5,h6{ font-weight: bold; }
42 | h1{ font-size:2.5em; }
43 | h2{ font-size:2em; }
44 | h3{ font-size:1.5em; }
45 | h4{ font-size:1.2em; }
46 | h5{ font-size:1em; }
47 | h6{ font-size:0.9em; }
48 |
49 | blockquote{
50 | color:#666666;
51 | margin:0;
52 | padding-left: 3em;
53 | border-left: 0.5em #EEE solid;
54 | }
55 | hr { display: block; height: 2px; border: 0; border-top: 1px solid #aaa;border-bottom: 1px solid #eee; margin: 1em 0; padding: 0; }
56 | pre, code, kbd, samp { color: #000; font-family: monospace, monospace; _font-family: 'courier new', monospace; font-size: 0.98em; }
57 | pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; }
58 |
59 | b, strong { font-weight: bold; }
60 |
61 | dfn { font-style: italic; }
62 |
63 | ins { background: #ff9; color: #000; text-decoration: none; }
64 |
65 | mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; }
66 |
67 | sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
68 | sup { top: -0.5em; }
69 | sub { bottom: -0.25em; }
70 |
71 | ul, ol { margin: 1em 0; padding: 0 0 0 2em; }
72 | li p:last-child { margin:0 }
73 | dd { margin: 0 0 0 2em; }
74 |
75 | img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; }
76 |
77 | table { border-collapse: collapse; border-spacing: 0; }
78 | td { vertical-align: top; }
79 |
80 | @media only screen and (min-width: 480px) {
81 | body{font-size:14px;}
82 | }
83 |
84 | @media only screen and (min-width: 768px) {
85 | body{font-size:16px;}
86 | }
87 |
88 | @media print {
89 | * { background: transparent !important; color: black !important; filter:none !important; -ms-filter: none !important; }
90 | body{font-size:12pt; max-width:100%;}
91 | a, a:visited { text-decoration: underline; }
92 | hr { height: 1px; border:0; border-bottom:1px solid black; }
93 | a[href]:after { content: " (" attr(href) ")"; }
94 | abbr[title]:after { content: " (" attr(title) ")"; }
95 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
96 | pre, blockquote { border: 1px solid #999; padding-right: 1em; page-break-inside: avoid; }
97 | tr, img { page-break-inside: avoid; }
98 | img { max-width: 100% !important; }
99 | @page :left { margin: 15mm 20mm 15mm 10mm; }
100 | @page :right { margin: 15mm 10mm 15mm 20mm; }
101 | p, h2, h3 { orphans: 3; widows: 3; }
102 | h2, h3 { page-break-after: avoid; }
103 | }
104 |
105 | div.highlight {
106 | background-color: #f8f8ff;
107 | border: 1px solid #DDD;
108 | padding: 15px;
109 | font-size: 14px;
110 | line-height: 16px;
111 | font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
112 | }
113 |
114 | /* pygmentize -S default -f html */
115 | .hll { background-color: #ffffcc }
116 | .c { color: #408080; font-style: italic } /* Comment */
117 | .err { border: 1px solid #FF0000 } /* Error */
118 | .k { color: #008000; font-weight: bold } /* Keyword */
119 | .o { color: #666666 } /* Operator */
120 | .cm { color: #408080; font-style: italic } /* Comment.Multiline */
121 | .cp { color: #BC7A00 } /* Comment.Preproc */
122 | .c1 { color: #408080; font-style: italic } /* Comment.Single */
123 | .cs { color: #408080; font-style: italic } /* Comment.Special */
124 | .gd { color: #A00000 } /* Generic.Deleted */
125 | .ge { font-style: italic } /* Generic.Emph */
126 | .gr { color: #FF0000 } /* Generic.Error */
127 | .gh { color: #000080; font-weight: bold } /* Generic.Heading */
128 | .gi { color: #00A000 } /* Generic.Inserted */
129 | .go { color: #808080 } /* Generic.Output */
130 | .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
131 | .gs { font-weight: bold } /* Generic.Strong */
132 | .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
133 | .gt { color: #0040D0 } /* Generic.Traceback */
134 | .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
135 | .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
136 | .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
137 | .kp { color: #008000 } /* Keyword.Pseudo */
138 | .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
139 | .kt { color: #B00040 } /* Keyword.Type */
140 | .m { color: #666666 } /* Literal.Number */
141 | .s { color: #BA2121 } /* Literal.String */
142 | .na { color: #7D9029 } /* Name.Attribute */
143 | .nb { color: #008000 } /* Name.Builtin */
144 | .nc { color: #0000FF; font-weight: bold } /* Name.Class */
145 | .no { color: #880000 } /* Name.Constant */
146 | .nd { color: #AA22FF } /* Name.Decorator */
147 | .ni { color: #999999; font-weight: bold } /* Name.Entity */
148 | .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
149 | .nf { color: #0000FF } /* Name.Function */
150 | .nl { color: #A0A000 } /* Name.Label */
151 | .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
152 | .nt { color: #008000; font-weight: bold } /* Name.Tag */
153 | .nv { color: #19177C } /* Name.Variable */
154 | .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
155 | .w { color: #bbbbbb } /* Text.Whitespace */
156 | .mf { color: #666666 } /* Literal.Number.Float */
157 | .mh { color: #666666 } /* Literal.Number.Hex */
158 | .mi { color: #666666 } /* Literal.Number.Integer */
159 | .mo { color: #666666 } /* Literal.Number.Oct */
160 | .sb { color: #BA2121 } /* Literal.String.Backtick */
161 | .sc { color: #BA2121 } /* Literal.String.Char */
162 | .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
163 | .s2 { color: #BA2121 } /* Literal.String.Double */
164 | .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
165 | .sh { color: #BA2121 } /* Literal.String.Heredoc */
166 | .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
167 | .sx { color: #008000 } /* Literal.String.Other */
168 | .sr { color: #BB6688 } /* Literal.String.Regex */
169 | .s1 { color: #BA2121 } /* Literal.String.Single */
170 | .ss { color: #19177C } /* Literal.String.Symbol */
171 | .bp { color: #008000 } /* Name.Builtin.Pseudo */
172 | .vc { color: #19177C } /* Name.Variable.Class */
173 | .vg { color: #19177C } /* Name.Variable.Global */
174 | .vi { color: #19177C } /* Name.Variable.Instance */
175 | .il { color: #666666 } /* Literal.Number.Integer.Long */
176 |
--------------------------------------------------------------------------------
/hal_specification.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: default
3 | ---
4 | # HAL - Hypertext Application Language
5 | ## A lean hypermedia type
6 |
7 | * __Author:__ [Mike Kelly][1] <[mike@stateless.co](mailto:mike@stateless.co)>
8 | * __Created:__ 2011-06-13
9 | * __Updated:__ 2013-09-18 (Updated)
10 |
11 | ## Summary
12 | HAL is a simple format that gives a consistent and easy way to
13 | hyperlink between resources in your API.
14 |
15 | Adopting HAL will make your API explorable, and its documentation easily
16 | discoverable from within the API itself. In short, it will make your API
17 | easier to work with and therefore more attractive to client developers.
18 |
19 | APIs that adopt HAL can be easily served and consumed using open source
20 | libraries available for most major programming languages. It's also
21 | simple enough that you can just deal with it as you would any other
22 | JSON.
23 |
24 | ## About The Author
25 | Mike Kelly is a software engineer from the UK. He runs an [API
26 | consultancy][1] helping companies design and build beautiful APIs that
27 | developers love.
28 |
29 | ## Quick links
30 | * [A demo API using HAL called HAL Talk][12]
31 | * [A list of libraries for working with HAL (Obj-C, Ruby, JS, PHP, C#,
32 | etc.)][14]
33 | * [A list of public hypermedia APIs using HAL][15]
34 | * [Discussion group (questions, feedback, etc)][2]
35 |
36 | ## General Description
37 | HAL provides a set of conventions for expressing hyperlinks in either
38 | JSON or XML.
39 |
40 | **The rest of a HAL document is just plain old JSON or XML.**
41 |
42 | Instead of using ad-hoc structures, or spending valuable time designing
43 | your own format; you can adopt HAL's conventions and focus on building
44 | and documenting the data and transitions that make up your API.
45 |
46 | HAL is a little bit like HTML for machines, in that it is generic and
47 | designed to drive many different types of application via hyperlinks.
48 | The difference is that HTML has features for helping 'human actors' move
49 | through a web application to achieve their goals, whereas HAL is
50 | intended for helping 'automated actors' move through a web API to
51 | achieve their goals.
52 |
53 | Having said that, **HAL is actually very human-friendly too**. Its
54 | conventions make the documentation for an API discoverable from the API
55 | messages themselves. This makes it possible for developers to jump
56 | straight into a HAL-based API and explore its capabilities, without the
57 | cognitive overhead of having to map some out-of-band documentation onto
58 | their journey.
59 |
60 | ## Examples
61 |
62 | The example below is how you might represent a collection of orders with
63 | hal+json. Things to look for:
64 |
65 | * The URI of the main resource being represented ('/orders') expressed
66 | with a self link
67 | * The 'next' link pointing to the next page of orders
68 | * A templated link called 'ea:find' for searching orders by id
69 | * The multiple 'ea:admin' link objects contained in an array
70 | * Two properties of the orders collection; 'currentlyProcessing' and
71 | 'shippedToday'
72 | * Embedded order resources with their own links and properties
73 | * The compact URI (curie) named 'ea' for expanding the name of the
74 | links to their documentation URL
75 |
76 | ### application/hal+json
77 | ```javascript
78 | {
79 | "_links": {
80 | "self": { "href": "/orders" },
81 | "curies": [{ "name": "ea", "href": "http://example.com/docs/rels/{rel}", "templated": true }],
82 | "next": { "href": "/orders?page=2" },
83 | "ea:find": {
84 | "href": "/orders{?id}",
85 | "templated": true
86 | },
87 | "ea:admin": [{
88 | "href": "/admins/2",
89 | "title": "Fred"
90 | }, {
91 | "href": "/admins/5",
92 | "title": "Kate"
93 | }]
94 | },
95 | "currentlyProcessing": 14,
96 | "shippedToday": 20,
97 | "_embedded": {
98 | "ea:order": [{
99 | "_links": {
100 | "self": { "href": "/orders/123" },
101 | "ea:basket": { "href": "/baskets/98712" },
102 | "ea:customer": { "href": "/customers/7809" }
103 | },
104 | "total": 30.00,
105 | "currency": "USD",
106 | "status": "shipped"
107 | }, {
108 | "_links": {
109 | "self": { "href": "/orders/124" },
110 | "ea:basket": { "href": "/baskets/97213" },
111 | "ea:customer": { "href": "/customers/12369" }
112 | },
113 | "total": 20.00,
114 | "currency": "USD",
115 | "status": "processing"
116 | }]
117 | }
118 | }
119 | ```
120 |
121 | ## The HAL Model
122 |
123 | The HAL conventions revolve around representing two simple concepts: _Resources_ and _Links_.
124 |
125 | ### Resources
126 | Resources have:
127 |
128 | * Links (to URIs)
129 | * Embedded Resources (i.e. other resources contained within them)
130 | * State (your bog standard JSON or XML data)
131 |
132 | ### Links
133 | Links have:
134 |
135 | * A target (a URI)
136 | * A relation aka. 'rel' (the name of the link)
137 | * A few other optional properties to help with deprecation, content
138 | negotiation, etc.
139 |
140 | Below is an image that roughly illustrates how a HAL representation is
141 | structured:
142 |
143 | ![The HAL Information model][4]
144 |
145 | ## How HAL is used in APIs
146 | HAL is designed for building APIs in which clients navigate around the
147 | resources by following links.
148 |
149 | Links are identified by link relations. Link relations are the lifeblood
150 | of a hypermedia API: they are how you tell client developers about
151 | what resources are available and how they can be interacted with, and
152 | they are how the code they write will select which link to traverse.
153 |
154 | Link relations are not just an identifying string in HAL, though. They
155 | are actually URLs, which developers can follow in order to read the
156 | documentation for a given link. This is what is known as
157 | "discoverability". The idea is that a developer can enter into your API,
158 | read through documentation for the available links, and then
159 | follow-their-nose through the API.
160 |
161 | HAL encourages the use of link relations to:
162 |
163 | * Identify links and embedded resources within the representation
164 | * Infer the expected structure and meaning of target resources
165 | * Signalling what requests and representations can be submitted to target resources
166 |
167 | ## How to serve HAL
168 | HAL has a media type for both the JSON and XML variants, whos names are
169 | `application/hal+json` and `application/hal+xml` respectively.
170 |
171 | When serving HAL over HTTP, the `Content-Type` of the response should
172 | contain the relevant media type name.
173 |
174 | ## The structure of a HAL document
175 |
176 | ### Minimum valid document
177 | A HAL document must at least contain an empty resource.
178 |
179 | An empty JSON object:
180 |
181 | ```javascript
182 | {}
183 | ```
184 |
185 | ### Resources
186 | In most cases, resources should have a self URI
187 |
188 | Represented via a 'self' link:
189 |
190 | ```javascript
191 | {
192 | "_links": {
193 | "self": { "href": "/example_resource" }
194 | }
195 | }
196 | ```
197 |
198 | ### Links
199 | Links must be contained directly within a resource:
200 |
201 | Links are represented as JSON object contained within a `_links` hash
202 | that must be a direct property of a resource object:
203 |
204 | ```javascript
205 | {
206 | "_links": {
207 | "next": { "href": "/page=2" }
208 | }
209 | }
210 | ```
211 |
212 | #### Link Relations
213 | Links have a relation (aka. 'rel'). This indicates the semantic -
214 | the meaning - of a particular link.
215 |
216 | Link rels are the main way of distinguishing between a resource's links.
217 |
218 | It's basically just a key within the `_links` hash, associating the link meaning
219 | (the 'rel') with the link object that contains data like the actual 'href' value:
220 |
221 | ```javascript
222 | {
223 | "_links": {
224 | "next": { "href": "/page=2" }
225 | }
226 | }
227 | ```
228 |
229 | #### API Discoverability
230 | Link rels should be URLs which reveal documentation about the
231 | given link, making them "discoverable". URLs are generally quite long
232 | and a bit nasty for use as keys. To get around this, HAL provides
233 | "CURIEs" which are basically named tokens that you can define in the
234 | document and use to express link relation URIs in a friendlier, more
235 | compact fashion i.e. `ex:widget` instead of
236 | `http://example.com/rels/widget`. The details are available in the
237 | section on CURIEs a bit further down.
238 |
239 | ### Representing Multiple Links With The Same Relation
240 | A resource may have multiple links that share the same link relation.
241 |
242 | For link relations that may have multiple links, we use an array of
243 | links.
244 |
245 | ```javascript
246 | {
247 | "_links": {
248 | "items": [{
249 | "href": "/first_item"
250 | },{
251 | "href": "/second_item"
252 | }]
253 | }
254 | }
255 | ```
256 |
257 | **Note:** If you're unsure whether the link should be singular, assume it
258 | will be multiple. If you pick singular and find you need to change it,
259 | you will need to create a new link relation or face breaking existing
260 | clients.
261 |
262 | ### CURIEs
263 |
264 | "CURIE"s help providing links to resource documentation.
265 |
266 | HAL gives you a reserved link relation 'curies' which you can use to hint at the location of resource documentation.
267 |
268 | ```javascript
269 | "_links": {
270 | "curies": [
271 | {
272 | "name": "doc",
273 | "href": "http://haltalk.herokuapp.com/docs/{rel}",
274 | "templated": true
275 | }
276 | ],
277 |
278 | "doc:latest-posts": {
279 | "href": "/posts/latest"
280 | }
281 | }
282 | ```
283 |
284 | There can be multiple links in the 'curies' section. They come with a 'name' and a templated 'href' which must
285 | contain the `{rel}` placeholder.
286 |
287 | Links in turn can then prefix their 'rel' with a CURIE name. Associating the `latest-posts` link with the `doc`
288 | documentation CURIE results in a link 'rel' set to `doc:latest-posts`.
289 |
290 | To retrieve documentation about the `latest-posts` resource, the client will expand the associated CURIE link
291 | with the actual link's 'rel'. This would result in a URL `http://haltalk.herokuapp.com/docs/latest-posts` which
292 | is expected to return documentation about this resource.
293 |
294 |
295 | ## To be continued...
296 | This relatively informal specification of HAL is incomplete and still in
297 | progress. For now, if you would like to have a full understanding please
298 | read the [formal specification][13].
299 |
300 | ## RFC
301 | The JSON variant of HAL (application/hal+json) has now been published as
302 | an internet draft: [draft-kelly-json-hal][13].
303 |
304 | ## Acknowledgements
305 |
306 | * Darrel Miller
307 | * Mike Amundsen
308 | * Mark Derricutt
309 | * Herman Radtke
310 | * Will Hartung
311 | * Steve Klabnik
312 | * everyone on hal-discuss
313 |
314 | Thanks for the help :)
315 |
316 | ## Notes/todo
317 |
318 | [1]: http://stateless.co/
319 | [2]: http://groups.google.com/group/hal-discuss
320 | [3]: http://blog.stateless.co/post/13296666138/json-linking-with-hal
321 | [4]: http://stateless.co/info-model.png
322 | [5]: http://tools.ietf.org/html/rfc2119
323 | [6]: http://tools.ietf.org/html/rfc5988#section-5.1
324 | [7]: http://tools.ietf.org/html/rfc5988
325 | [8]: http://tools.ietf.org/html/rfc5988#section-5.3
326 | [9]: http://tools.ietf.org/html/rfc5988#section-5.4
327 | [10]: http://www.w3.org/TR/curie/
328 | [11]: http://tools.ietf.org/html/rfc6570
329 | [12]: http://haltalk.herokuapp.com/
330 | [13]: http://tools.ietf.org/html/draft-kelly-json-hal
331 | [14]: https://github.com/mikekelly/hal_specification/wiki/Libraries
332 | [15]: https://github.com/mikekelly/hal_specification/wiki/APIs
333 |
--------------------------------------------------------------------------------
/info-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikekelly/hal_specification/4489093925ffaa13bd8be5af2f20caa8dcf1f142/info-model.png
--------------------------------------------------------------------------------
/resource_blot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikekelly/hal_specification/4489093925ffaa13bd8be5af2f20caa8dcf1f142/resource_blot.png
--------------------------------------------------------------------------------