@modernpoacher/halacious 6.3.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,10 +4,11 @@ A HAL processor for Hapi.
4
4
 
5
5
  ## Campatibility
6
6
 
7
- Compatible with Hapi v18.
7
+ Compatible with Hapi v21.
8
8
 
9
9
  ## Overview
10
- *Halacious* is a plugin for the HapiJS web application server that makes **HATEOASIFYING** your app ridiculously
10
+
11
+ _Halacious_ is a plugin for the HapiJS web application server that makes **HATEOASIFYING** your app ridiculously
11
12
  easy. When paired with a well-aged HAL client-side library, you will feel the warmth of loose API coupling and the feeling
12
13
  of moral superiorty as you rid your SPA of hard-coded api links.
13
14
 
@@ -16,6 +17,7 @@ secret sauce. Halacious embraces Hapi's configuration-centric approach to applic
16
17
  be accomplished without writing any code at all.
17
18
 
18
19
  ## Features
20
+
19
21
  - Dead-simple namespace/rel registration system that encourages you to document your API as you go
20
22
  - Automatic api root REST endpoint generation that instantly gives you links to all top-level API endpoints
21
23
  - Automatic rel documentation site generator so that your fully resolved rel names actually, you know, point somewhere.
@@ -27,162 +29,181 @@ be accomplished without writing any code at all.
27
29
  - Bunches of unit tests
28
30
 
29
31
  ## Getting Started
32
+
30
33
  Start by npm installing Halicious into your Hapi project folder:
31
34
 
32
- ```
35
+ ```shell
33
36
  npm install @modernpoacher/halacious --save
34
37
  ```
35
38
 
36
39
  or
37
40
 
38
- ```
41
+ ```shell
39
42
  yarn add @modernpoacher/halacious --dev
40
43
  ```
41
44
 
42
45
  Register the plugin with the app server
43
- ```javascript
44
- const hapi = require('@hapi/hapi');
45
- const halacious = require('#halacious');
46
46
 
47
- const server = hapi.server({ port: 8080 });
47
+ ```javascript
48
+ import hapi from '@hapi/hapi'
49
+ import halacious from '#halacious'
48
50
 
49
51
  async function init () {
50
- await server.register(halacious)
52
+ const server = hapi.server({ port: 8080 })
51
53
 
52
- server.route({
53
- method: 'get',
54
- path: '/hello/{name}',
55
- handler: function (req) {
56
- return { message: 'Hello, '+req.params.name };
57
- }
58
- });
54
+ await server.register(halacious)
55
+
56
+ server.route({
57
+ method: 'get',
58
+ path: '/hello/{name}',
59
+ handler (req) {
60
+ return { message: 'Hello, '+req.params.name }
61
+ }
62
+ })
59
63
 
60
- await server.start()
64
+ await server.start()
61
65
 
62
- console.info('Server started at %s', server.info.uri);
66
+ console.info('Server started at %s', server.info.uri)
63
67
  }
64
68
 
65
- init();
69
+ init()
66
70
  ```
71
+
67
72
  Launch the server:
68
- ```
73
+
74
+ ```shell
69
75
  node ./examples/hello-world
70
76
  ```
71
77
 
72
78
  Make a request
73
- ```
79
+
80
+ ```shell
74
81
  curl -H 'Accept: application/hal+json' http://localhost:8080/hello/world
75
82
  ```
76
83
 
77
84
  See the response
78
- ```
85
+
86
+ ```json
79
87
  {
80
- "_links": {
81
- "self": {
82
- "href": "/hello/world"
83
- }
84
- },
85
- "message": "Hello, world"
88
+ "_links": {
89
+ "self": {
90
+ "href": "/hello/world"
91
+ }
92
+ },
93
+ "message": "Hello, world"
86
94
  }
87
95
  ```
96
+
88
97
  ## Linking
98
+
89
99
  Links may be declared directly within the route config.
100
+
90
101
  ```javascript
91
102
  server.route({
92
- method: 'get',
93
- path: '/users/{userId}',
94
- handler: function (req) {
95
- return { id: req.params.userId, name: 'User ' + req.params.userId, googlePlusId: '107835557095464780852' };
96
- },
97
- config: {
98
- plugins: {
99
- hal: {
100
- links: {
101
- 'home': 'http://plus.google.com/{googlePlusId}'
102
- },
103
- ignore: 'googlePlusId' // remove the id property from the response
104
- }
105
- }
103
+ method: 'get',
104
+ path: '/users/{userId}',
105
+ handler (req) {
106
+ return { id: req.params.userId, name: 'User ' + req.params.userId, googlePlusId: '107835557095464780852' }
107
+ },
108
+ config: {
109
+ plugins: {
110
+ hal: {
111
+ links: {
112
+ 'home': 'http://plus.google.com/{googlePlusId}'
113
+ },
114
+ ignore: 'googlePlusId' // remove the id property from the response
115
+ }
106
116
  }
107
- });
108
- ```
117
+ }
118
+ })
109
119
  ```
120
+
121
+ ```shell
110
122
  curl -H 'Accept: application/hal+json' http://localhost:8080/users/100
111
123
  ```
124
+
112
125
  will produce:
113
- ```
126
+
127
+ ```json
114
128
  {
115
- "_links": {
116
- "self": {
117
- "href": "/users/1234"
118
- },
119
- "home": {
120
- "href": "http://plus.google.com/107835557095464780852"
121
- }
129
+ "_links": {
130
+ "self": {
131
+ "href": "/users/1234"
122
132
  },
123
- "id": "100",
124
- "name": "User 1234"
133
+ "home": {
134
+ "href": "http://plus.google.com/107835557095464780852"
135
+ }
136
+ },
137
+ "id": "100",
138
+ "name": "User 1234"
125
139
  }
126
140
  ```
127
141
 
128
142
  ## Embedding
143
+
129
144
  HAL allows you to conserve bandwidth by optionally embedding link payloads in the original request. Halacious will
130
145
  automatically convert nested objects into embedded HAL representations (if you ask nicely).
131
146
 
132
147
  ```javascript
133
148
  server.route({
134
- method: 'get',
135
- path: '/users/{userId}',
136
- handler: function (req) {
137
- return {
138
- id: req.params.userId,
139
- name: 'User ' + req.params.userId,
140
- boss: {
141
- id: 1234,
142
- name: 'Boss Man'
143
- }
144
- };
145
- },
146
- config: {
147
- plugins: {
148
- hal: {
149
- embedded: {
150
- 'boss': {
151
- path: 'boss', // the property name of the object to embed
152
- href: '../{item.id}'
153
- }
154
- }
155
- }
149
+ method: 'get',
150
+ path: '/users/{userId}',
151
+ handler (req) {
152
+ return {
153
+ id: req.params.userId,
154
+ name: 'User ' + req.params.userId,
155
+ boss: {
156
+ id: 1234,
157
+ name: 'Boss Man'
158
+ }
159
+ }
160
+ },
161
+ config: {
162
+ plugins: {
163
+ hal: {
164
+ embedded: {
165
+ 'boss': {
166
+ path: 'boss', // the property name of the object to embed
167
+ href: '../{item.id}'
168
+ }
156
169
  }
170
+ }
157
171
  }
158
- });
159
- ```
172
+ }
173
+ })
160
174
  ```
175
+
176
+ ```shell
161
177
  curl -H 'Accept: application/hal+json' http://localhost:8080/users/100
178
+ ```
179
+
180
+ will produce:
162
181
 
182
+ ```json
163
183
  {
164
- "_links": {
184
+ "_links": {
185
+ "self": {
186
+ "href": "/users/100"
187
+ }
188
+ },
189
+ "id": "100",
190
+ "name": "User 100",
191
+ "_embedded": {
192
+ "boss": {
193
+ "_links": {
165
194
  "self": {
166
- "href": "/users/100"
167
- }
168
- },
169
- "id": "100",
170
- "name": "User 100",
171
- "_embedded": {
172
- "boss": {
173
- "_links": {
174
- "self": {
175
- "href": "/users/1234"
176
- }
177
- },
178
- "id": 1234,
179
- "name": "Boss Man"
195
+ "href": "/users/1234"
180
196
  }
197
+ },
198
+ "id": 1234,
199
+ "name": "Boss Man"
181
200
  }
201
+ }
182
202
  }
183
203
  ```
184
204
 
185
205
  ## Programmatic configuration of HAL representations
206
+
186
207
  You may find the need to take the wheel on occasion and directly configure outbound representions. For example,
187
208
  some links may be conditional on potentially asynchronous criteria. Fortunately, Halacious provides two ways to do this:
188
209
 
@@ -190,130 +211,131 @@ some links may be conditional on potentially asynchronous criteria. Fortunately,
190
211
  2. By implementing a `toHal()` method directly on a wrapped entity.
191
212
 
192
213
  In either case, the method signature is the same: `fn(rep, callback)` where
214
+
193
215
  - `rep` - a representation object with the following properties and functions:
194
- - `factory` - a factory reference for creating new representations. The factory object implements one method:
195
- - `create(entity, selfHref)` - wraps entity with a new Hal representation, whose self link will point to selfHref
196
- - `request` - the originating hapi request
197
- - `self` - a shortcut to the representation's self link
198
- - `entity` - the original wrapped entity
199
- - `prop(name, value)` - manually adds a name/value pair to the representation
200
- - `merge(object)` - merges the properties of another object into the representation
201
- - `ignore(...propertyNames)` - prevents fields from being included in the response
202
- - `link(relName, href)` - adds a new link to the `_links` collection, returning the new link. Link objects support
203
- the following properties (See see http://tools.ietf.org/html/draft-kelly-json-hal-06#section-8.2 for more information):
204
- - `href`
205
- - `templated`
206
- - `title`
207
- - `type`
208
- - `deprecation`
209
- - `name`
210
- - `profile`
211
- - `hreflang`
212
- - `embed(rel, self, entity)` - adds an entity to the representation's `_embedded` collection with the supplied rel link relation and self href, returning a new representation
216
+ - `factory` - a factory reference for creating new representations. The factory object implements one method:
217
+ - `create(entity, selfHref)` - wraps entity with a new Hal representation, whose self link will point to selfHref
218
+ - `request` - the originating hapi request
219
+ - `self` - a shortcut to the representation's self link
220
+ - `entity` - the original wrapped entity
221
+ - `prop(name, value)` - manually adds a name/value pair to the representation
222
+ - `merge(object)` - merges the properties of another object into the representation
223
+ - `ignore(...propertyNames)` - prevents fields from being included in the response
224
+ - `link(relName, href)` - adds a new link to the `_links` collection, returning the new link. Link objects support
225
+ the following properties (See see http://tools.ietf.org/html/draft-kelly-json-hal-06#section-8.2 for more information): - `href` - `templated` - `title` - `type` - `deprecation` - `name` - `profile` - `hreflang`
226
+ - `embed(rel, self, entity)` - adds an entity to the representation's `_embedded` collection with the supplied rel link relation and self href, returning a new representation
213
227
  object for further configuration.
214
228
  - `callback([err], [representation])` - an asynchronous callback function to be called when configuration of the hal entity
215
- is complete. Most of the time this function should be called with no arguments. Only pass arguments if there has been
216
- an error or if a completely new representation has been created with `rep.factory.create()`.
229
+ is complete. Most of the time this function should be called with no arguments. Only pass arguments if there has been
230
+ an error or if a completely new representation has been created with `rep.factory.create()`.
217
231
 
218
232
  #### Example 1: A `prepare()` function declared in the route descriptor.
233
+
219
234
  ```javascript
220
235
  server.route({
221
- method: 'get',
222
- path: '/users',
223
- handler: function (req) {
224
- return {
225
- start: 0,
226
- count: 2,
227
- limit: 2,
228
- items: [
229
- { id: 100, firstName: 'Brad', lastName: 'Leupen', googlePlusId: '107835557095464780852'},
230
- { id: 101, firstName: 'Mark', lastName: 'Zuckerberg'}
231
- ]
232
- };
233
- },
234
- config: {
235
- plugins: {
236
- hal: {
237
- // you can also assign this function directly to the hal property above as a shortcut
238
- prepare: function (rep, next) {
239
- rep.entity.items.forEach(function (item) {
240
- var embed = rep.embed('item', './' + item.id, item);
241
- if (item.googlePlusId) {
242
- embed.link('home', 'http://plus.google.com/' + item.googlePlusId);
243
- embed.ignore('googlePlusId');
244
- }
245
- });
246
- rep.ignore('items');
247
- // dont forget to call next!
248
- next();
249
- }
236
+ method: 'get',
237
+ path: '/users',
238
+ handler (req) {
239
+ return {
240
+ start: 0,
241
+ count: 2,
242
+ limit: 2,
243
+ items: [
244
+ { id: 100, firstName: 'Brad', lastName: 'Leupen', googlePlusId: '107835557095464780852'},
245
+ { id: 101, firstName: 'Mark', lastName: 'Zuckerberg'}
246
+ ]
247
+ }
248
+ },
249
+ config: {
250
+ plugins: {
251
+ hal: {
252
+ // you can also assign this function directly to the hal property above as a shortcut
253
+ prepare (rep, next) {
254
+ rep.entity.items.forEach(function (item) {
255
+ const embed = rep.embed('item', './' + item.id, item)
256
+ if (item.googlePlusId) {
257
+ embed.link('home', 'http://plus.google.com/' + item.googlePlusId)
258
+ embed.ignore('googlePlusId')
250
259
  }
260
+ })
261
+ rep.ignore('items')
262
+ // dont forget to call next!
263
+ next()
251
264
  }
265
+ }
252
266
  }
253
- });
254
- ```
267
+ }
268
+ })
255
269
  ```
270
+
271
+ ```shell
256
272
  curl -H 'Accept: application/hal+json' http://localhost:8080/users
273
+ ```
257
274
 
275
+ will produce:
276
+
277
+ ```json
258
278
  {
259
- "_links": {
260
- "self": {
261
- "href": "/users"
262
- }
263
- },
264
- "start": 0,
265
- "count": 2,
266
- "limit": 2,
267
- "_embedded": {
268
- "item": [
269
- {
270
- "_links": {
271
- "self": {
272
- "href": "/users/100"
273
- },
274
- "home": {
275
- "href": "http://plus.google.com/107835557095464780852"
276
- }
277
- },
278
- "id": 100,
279
- "firstName": "Brad",
280
- "lastName": "Leupen"
281
- },
282
- {
283
- "_links": {
284
- "self": {
285
- "href": "/users/101"
286
- }
287
- },
288
- "id": 101,
289
- "firstName": "Mark",
290
- "lastName": "Zuckerberg"
291
- }
292
- ]
279
+ "_links": {
280
+ "self": {
281
+ "href": "/users"
293
282
  }
283
+ },
284
+ "start": 0,
285
+ "count": 2,
286
+ "limit": 2,
287
+ "_embedded": {
288
+ "item": [
289
+ {
290
+ "_links": {
291
+ "self": {
292
+ "href": "/users/100"
293
+ },
294
+ "home": {
295
+ "href": "http://plus.google.com/107835557095464780852"
296
+ }
297
+ },
298
+ "id": 100,
299
+ "firstName": "Brad",
300
+ "lastName": "Leupen"
301
+ },
302
+ {
303
+ "_links": {
304
+ "self": {
305
+ "href": "/users/101"
306
+ }
307
+ },
308
+ "id": 101,
309
+ "firstName": "Mark",
310
+ "lastName": "Zuckerberg"
311
+ }
312
+ ]
313
+ }
294
314
  }
295
315
  ```
316
+
296
317
  #### Example 2: Implementing `toHal()` on a domain entity:
318
+
297
319
  ```javascript
298
320
  function User(id, firstName, lastName, googlePlusId) {
299
- this.id = id;
300
- this.firstName = firstName;
301
- this.lastName = lastName;
302
- this.googlePlusId = googlePlusId;
321
+ this.id = id
322
+ this.firstName = firstName
323
+ this.lastName = lastName
324
+ this.googlePlusId = googlePlusId
303
325
  }
304
326
 
305
327
  User.prototype.toHal = function(rep, next) {
306
- if (this.googlePlusId) {
307
- rep.link('home', 'http://plus.google.com/' + this.googlePlusId);
308
- rep.ignore('googlePlusId');
309
- }
310
- next();
311
- };
328
+ if (this.googlePlusId) {
329
+ rep.link('home', 'http://plus.google.com/' + this.googlePlusId)
330
+ rep.ignore('googlePlusId')
331
+ }
332
+ next()
333
+ }
312
334
 
313
335
  server.route({
314
336
  method: 'get',
315
337
  path: '/users',
316
- handler: function (req) {
338
+ handler (req) {
317
339
  return {
318
340
  start: 0,
319
341
  count: 2,
@@ -322,7 +344,7 @@ server.route({
322
344
  new User(100, 'Brad', 'Leupen', '107835557095464780852'),
323
345
  new User(101, 'Mark', 'Zuckerberg')
324
346
  ]
325
- };
347
+ }
326
348
  },
327
349
  config: {
328
350
  plugins: {
@@ -336,121 +358,130 @@ server.route({
336
358
  }
337
359
  }
338
360
  }
339
- });
361
+ })
340
362
  ```
341
363
 
342
364
  ## The HAL route configuration object
365
+
343
366
  The `config.plugins.hal` route configuration object takes the following format:
367
+
344
368
  - A function `fn(rep, next)` - for purely programmatic control over the representation
345
- or
369
+ or
346
370
  - An object with the following properties:
347
- - `api` - an optional top level api rel name to assign to this route. Setting a value will cause this route to be included
348
- in the root api resource's _links collection.
349
- - `prepare(rep, next)` - an optional prepare function
350
- - `ignore` - A String or array of strings containing the names of properties to remove from the output. Can be used
371
+ - `api` - an optional top level api rel name to assign to this route. Setting a value will cause this route to be included
372
+ in the root api resource's \_links collection.
373
+ - `prepare(rep, next)` - an optional prepare function
374
+ - `ignore` - A String or array of strings containing the names of properties to remove from the output. Can be used
351
375
  to remove reduntant information from the response
352
- - `query` - An RFC 6570 compatible query string that should be communicated to your clients. See: http://tools.ietf.org/html/rfc6570.
376
+ - `query` - An RFC 6570 compatible query string that should be communicated to your clients. See: http://tools.ietf.org/html/rfc6570.
353
377
  Example: `{?q*,start,limit}`. These parameters will be included in top level api links. They will also be included in self links if supplied in the request.
354
378
  Query parameters that are not included in the template, such as runtime tokens, will be excluded from the self href.
355
- - `links` - An object whose keys are rel names and whose values are href strings or link objects that contain
356
- at least an `href` property. Hrefs may be absolute or relative to the representation's self link. Hrefs may also contain
357
- `{expression}` template expressions, which are resolved against the wrapped entity.
358
- - `embedded` An object whose keys are rel names and whose values are configuration objects with:
359
- - `path` - a path expression to evaluate against the wrapped entity to derive the object to embed.
360
- - `href` - a Function, String or link object that will be used to define the entity's self relation. Like links,
361
- embedded href's may also be templated. Unlike links, embedded href templates have access to two state variables:
362
- - `self` - the parent entity
363
- - `item` - the child entity
364
- - `links`
365
- - `embedded` (recursively evaluated)
366
- - `prepare(rep, next)`
367
- - `absolute` - a boolean true/false. if true, hrefs for this representation will include protocal, server, and port.
368
- Default: false
379
+ - `links` - An object whose keys are rel names and whose values are href strings or link objects that contain
380
+ at least an `href` property. Hrefs may be absolute or relative to the representation's self link. Hrefs may also contain
381
+ `{expression}` template expressions, which are resolved against the wrapped entity.
382
+ - `embedded` An object whose keys are rel names and whose values are configuration objects with:
383
+ - `path` - a path expression to evaluate against the wrapped entity to derive the object to embed.
384
+ - `href` - a Function, String or link object that will be used to define the entity's self relation. Like links,
385
+ embedded href's may also be templated. Unlike links, embedded href templates have access to two state variables: - `self` - the parent entity - `item` - the child entity
386
+ - `links`
387
+ - `embedded` (recursively evaluated)
388
+ - `prepare(rep, next)`
389
+ - `absolute` - a boolean true/false. if true, hrefs for this representation will include protocal, server, and port.
390
+ Default: false
369
391
 
370
392
  ## Namespaces and Rels
393
+
371
394
  So far, we have not done a real good job in our examples defining our link relations. Unless registered with the IANA,
372
395
  link relations should really be unique URLs that resolve to documentation regarding their semantics. Halacious will
373
396
  happily let you be lazy but its much better if we do things the Right Way.
374
397
 
375
398
  ### Manually creating a namespace
399
+
376
400
  Halacious exposes its api to your Hapi server so that you may configure it at runtime like so:
377
- ```javascript
378
- const hapi = require('@hapi/hapi');
379
- const halacious = require('#halacious');
401
+
402
+ ```javascript
403
+ import hapi from '@hapi/hapi'
404
+ import halacious from '#halacious'
380
405
 
381
406
  async function init () {
382
- await server.register(halacious);
383
-
384
- const namespace = server.plugins.halacious.namespaces.add({ name: 'mycompany', description: 'My Companys namespace', prefix: 'mco'});
385
- namespace.rel({ name: 'users', description: 'a collection of users' });
386
- namespace.rel({ name: 'user', description: 'a single user' });
387
- namespace.rel({ name: 'boss', description: 'a users boss' });
388
-
389
- server.route({
390
- method: 'get',
391
- path: '/users/{userId}',
392
- handler: function (req) {
393
- return { id: req.params.userId, name: 'User ' + req.params.userId, bossId: 200 };
394
- },
395
- config: {
396
- plugins: {
397
- hal: {
398
- links: {
399
- 'mco:boss': '../{bossId}'
400
- },
401
- ignore: 'bossId'
402
- }
403
- }
407
+ const server = hapi.server({ port: 8080 })
408
+
409
+ await server.register(halacious)
410
+
411
+ const namespace = server.plugins.halacious.namespaces.add({ name: 'mycompany', description: 'My Companys namespace', prefix: 'mco'})
412
+ namespace.rel({ name: 'users', description: 'a collection of users' })
413
+ namespace.rel({ name: 'user', description: 'a single user' })
414
+ namespace.rel({ name: 'boss', description: 'a users boss' })
415
+
416
+ server.route({
417
+ method: 'get',
418
+ path: '/users/{userId}',
419
+ handler (req) {
420
+ return { id: req.params.userId, name: 'User ' + req.params.userId, bossId: 200 }
421
+ },
422
+ config: {
423
+ plugins: {
424
+ hal: {
425
+ links: {
426
+ 'mco:boss': '../{bossId}'
427
+ },
428
+ ignore: 'bossId'
404
429
  }
405
- });
430
+ }
431
+ }
432
+ })
406
433
  }
407
434
 
408
435
  init()
409
436
  ```
410
- Now, when we access the server we see a new type of link in the `_links` collection, `curies`. The curies link provides a mechanism
411
- to use shorthand rel names while preserving their uniqueness. Without the curie, the 'mco:boss' rel key would be expanded
412
- to read `/rels/mycompany/boss`
413
-
414
- ```
415
- curl -H 'Accept: application/hal+json' http://localhost:8080/users/100
416
-
417
- {
418
- "_links": {
419
- "self": {
420
- "href": "/users/100"
421
- },
422
- "curies": [
423
- {
424
- "name": "mco",
425
- "href": "/rels/mycompany/{rel}",
426
- "templated": true
427
- }
428
- ],
429
- "mco:boss": {
430
- "href": "/users/200"
431
- }
432
- },
433
- "id": "100",
434
- "name": "User 100"
435
- }
436
- ```
437
+
438
+ Now, when we access the server we see a new type of link in the `_links` collection, `curies`. The curies link provides a mechanism to use shorthand rel names while preserving their uniqueness. Without the curie, the 'mco:boss' rel key would be expanded to read `/rels/mycompany/boss`
439
+
440
+ ```shell
441
+ curl -H 'Accept: application/hal+json' http://localhost:8080/users/100
442
+ ```
443
+
444
+ will produce:
445
+
446
+ ```json
447
+ {
448
+ "_links": {
449
+ "self": {
450
+ "href": "/users/100"
451
+ },
452
+ "curies": [
453
+ {
454
+ "name": "mco",
455
+ "href": "/rels/mycompany/{rel}",
456
+ "templated": true
457
+ }
458
+ ],
459
+ "mco:boss": {
460
+ "href": "/users/200"
461
+ }
462
+ },
463
+ "id": "100",
464
+ "name": "User 100"
465
+ }
466
+ ```
437
467
 
438
468
  ### Creating a namespace from a folder of documentated rels
469
+
439
470
  In our examples folder, we have created a folder `rels/mycompany` containing markdown documents for all of the rels in our
440
471
  company's namespace. We can suck all these into the system in one fell swoop:
441
472
 
442
473
  ```javascript
443
- const hapi = require('@hapi/hapi');
444
- const halacious = require('#halacious');
474
+ import hapi from '@hapi/hapi'
475
+ import halacious from '#halacious'
445
476
 
446
477
  async function init () {
447
- const server = hapi.server({ port: 8080 });
478
+ const server = hapi.server({ port: 8080 })
448
479
 
449
- await server.register(halacious);
480
+ await server.register(halacious)
450
481
 
451
- server.plugins.halacious.namespaces.add({ dir: __dirname + '/rels/mycompany', prefix: 'mco' })
482
+ server.plugins.halacious.namespaces.add({ dir: __dirname + '/rels/mycompany', prefix: 'mco' })
452
483
 
453
- await server.start()
484
+ await server.start()
454
485
  }
455
486
 
456
487
  init()
@@ -459,59 +490,61 @@ init()
459
490
  Ideally these documents should provide your api consumer enough semantic information to navigate your api.
460
491
 
461
492
  ## Rels documentation
493
+
462
494
  Halacious includes an (extremely) barebones namespace / rel navigator for users to browse your documentation.
463
495
  The server binds to the `/rels` path on your server by default.
464
496
 
465
- _Note: Hapi 9 / 10 users must install and configure the vision views plugin to enable this feature.
497
+ \_Note: Hapi 9 / 10 users must install and configure the vision views plugin to enable this feature.
466
498
 
467
499
  ## Automatic /api root
500
+
468
501
  Discoverability is a key tenant of any hypermedia system. HAL requires that only the root API url be known to clients of your
469
502
  application, from which all other urls may be derived via rel names. If you want, Halacious will create this root api
470
503
  route for you automatically. All you need to do is to identify which resources to include by using the `api` route
471
504
  configuration option. For example:
472
505
 
473
506
  ```javascript
474
- const hapi = require('@hapi/hapi');
475
- const halacious = require('#halacious');
507
+ import hapi from '@hapi/hapi'
508
+ import halacious from '#halacious'
476
509
 
477
510
  async function init () {
478
- const server = hapi.server({ port: 8080 });
511
+ const server = hapi.server({ port: 8080 })
479
512
 
480
- await server.register(halacious);
513
+ await server.register(halacious)
481
514
 
482
- const namespace = server.plugins.halacious.namespaces.add({ name: 'mycompany', description: 'My Companys namespace', prefix: 'mco'});
483
- namespace.rel({ name: 'users', description: 'a collection of users' });
484
- namespace.rel({ name: 'user', description: 'a single user' });
515
+ const namespace = server.plugins.halacious.namespaces.add({ name: 'mycompany', description: 'My Companys namespace', prefix: 'mco'})
516
+ namespace.rel({ name: 'users', description: 'a collection of users' })
517
+ namespace.rel({ name: 'user', description: 'a single user' })
485
518
 
486
- server.route({
487
- method: 'get',
488
- path: '/users',
489
- handler: function () {
490
- return {};
491
- },
492
- config: {
493
- plugins: {
494
- hal: {
495
- api: 'mco:users'
496
- }
497
- }
519
+ server.route({
520
+ method: 'get',
521
+ path: '/users',
522
+ handler () {
523
+ return {}
524
+ },
525
+ config: {
526
+ plugins: {
527
+ hal: {
528
+ api: 'mco:users'
498
529
  }
499
- });
530
+ }
531
+ }
532
+ })
500
533
 
501
- server.route({
502
- method: 'get',
503
- path: '/users/{userId}',
504
- handler: function () {
505
- return {};
506
- },
507
- config: {
508
- plugins: {
509
- hal: {
510
- api: 'mco:user'
511
- }
512
- }
534
+ server.route({
535
+ method: 'get',
536
+ path: '/users/{userId}',
537
+ handler () {
538
+ return {}
539
+ },
540
+ config: {
541
+ plugins: {
542
+ hal: {
543
+ api: 'mco:user'
513
544
  }
514
- });
545
+ }
546
+ }
547
+ })
515
548
  }
516
549
 
517
550
  init()
@@ -519,35 +552,40 @@ init()
519
552
 
520
553
  will auto-create the following api root:
521
554
 
522
- ```
555
+ ```shell
523
556
  curl -H 'Accept: application/hal+json' http://localhost:8080/api/
557
+ ```
524
558
 
559
+ will produce:
560
+
561
+ ```json
525
562
  {
526
- "_links": {
527
- "self": {
528
- "href": "/api/"
529
- },
530
- "curies": [
531
- {
532
- "name": "mco",
533
- "href": "/rels/mycompany/{rel}",
534
- "templated": true
535
- }
536
- ],
537
- "mco:users": {
538
- "href": "/users"
539
- },
540
- "mco:user": {
541
- "href": "/users/{userId}",
542
- "templated": true
543
- }
563
+ "_links": {
564
+ "self": {
565
+ "href": "/api/"
566
+ },
567
+ "curies": [
568
+ {
569
+ "name": "mco",
570
+ "href": "/rels/mycompany/{rel}",
571
+ "templated": true
572
+ }
573
+ ],
574
+ "mco:users": {
575
+ "href": "/users"
576
+ },
577
+ "mco:user": {
578
+ "href": "/users/{userId}",
579
+ "templated": true
544
580
  }
581
+ }
545
582
  }
546
583
  ```
547
584
 
548
585
  ## Plugin Options
586
+
549
587
  - `strict` - setting this to `true` will cause an exception to be thrown when referencing unregistered local rel. Setting this
550
- to true will help catch typos during development. Default: `false`
588
+ to true will help catch typos during development. Default: `false`
551
589
  - `relsPath` - the route path to the rels documentation root. Default: `/rels`
552
590
  - `relsAuth` - the hapi authentication setting to use for the documentation routes. Default: `false`
553
591
  - `relsTemplate` - a boolean true/false. if true, rels documentation uses the default template. Default: `true`
@@ -557,7 +595,7 @@ to true will help catch typos during development. Default: `false`
557
595
  - `apiServerLabel` - when set, Halacious will select for a specific server to route the api root.
558
596
  - `mediaTypes` - an array of media types that will trigger the hal processor to modify the response (e.g. `['application/json',
559
597
  'application/hal+json']`). the media types are checked in order. if any match the accept header parameters, then the
560
- response will be halified and the media type of the response will be set to the first winner. Default: `['application/hal+json']`
598
+ response will be halified and the media type of the response will be set to the first winner. Default: `['application/hal+json']`
561
599
  - `absolute` - a boolean true/false. if true, all hrefs will include the protocol, server, and port. Default: false
562
600
  - `host` - a hostname+port string for all absolute link urls
563
601
  - `hostname` - a string hostname for all absolute link urls (only used if host is blank)