UNPKG

8.94 kBMarkdownView Raw
1# bissle
2#### Minimalist HALicious pagination reply interface for [HapiJS](https://github.com/hapijs/hapi)
3
4[![Travis](https://img.shields.io/travis/felixheck/bissle.svg)](https://travis-ci.org/felixheck/bissle/builds/) ![npm](https://img.shields.io/npm/dt/bissle.svg)
5
6---
7
81. [Introduction](#introduction)
92. [Installation](#installation)
103. [Usage](#usage)
114. [API](#api)
125. [Example](#example)
136. [Testing](#testing)
147. [Contribution](#contribution)
158. [License](#license)
16
17## Introduction
18
19This [HapiJS](https://github.com/hapijs/hapi) plugin enables an additional reply interface to paginate a response in a RESTful and [HAL](https://tools.ietf.org/html/draft-kelly-json-hal-06) compliant manner. So the plugin accordingly splices the initial response; extends it with meta information about the count of entries per page, the total count and the current page; adds a link map for HALicious navigation and appends the corresponding `Link` header. It is not a middleware-like plugin, so you are allowed to control the usage explicitly by yourself. Because of this, it works perfectly in combination with HAL plugins like [halacious](https://github.com/bleupen/halacious), as it is shown in the [example](#example) below.
20
21The plugin is implemented in ECMAScript 6, therefore the development dependencies are based on `babel`. Additionally `eslint` and `tape` are used to grant a high quality implementation.
22
23**bissle** is the Swabian term for *a little bit*, it should visualize the sense of pagination.
24
25## Installation
26For installation use the [Node Package Manager](https://github.com/npm/npm):
27```
28// production version with ES5 syntax
29$ npm install --save bissle
30```
31
32or clone the repository:
33```
34// development version with ES6 syntax
35$ git clone https://github.com/felixheck/bissle
36```
37
38Alternatively use the [Yarn Package Manager](https://yarnpkg.com):
39```
40// production version with ES5 syntax
41$ yarn add bissle
42```
43
44## Usage
45#### Import
46First you have to import the module and the peer dependency [akaya](https://github.com/felixheck/akaya):
47``` js
48const bissle = require('bissle');
49const akaya = require('akaya');
50```
51
52#### Create HapiJS server
53Afterwards create your **HapiJS** server and the corresponding connection if not already done:
54``` js
55const server = new Hapi.Server();
56
57server.connection({
58 port: 1337,
59 host: 'localhost',
60});
61```
62
63#### Registration
64Finally register the plugins per `server.register()`:
65``` js
66server.register([akaya, bissle], err => {
67 if (err) {
68 throw err;
69 }
70
71 server.start();
72});
73```
74
75After registering **bissle**, the [HapiJS reply interface](http://hapijs.com/api#reply-interface) will be decorated with the new method `reply.bissle()`.
76
77#### Joi Validation
78If you use **Joi** for request validation, simply add `per_page` and `page` to the query scheme. The plugin exposes the all *bissle* related scheme via `server.plugins.bissle.scheme`. Alternatively it is possible to enable the `allowUnknown` option.<br>The exposed object contains additionally the scheme for plugin related options.
79
80## API
81#### Plugin Options
82While the plugin registration it is possible to pass a [plugin specific options object](http://hapijs.com/api#serverregisterplugins-options-callback):
83- `options {Object}` - The plugin specific options object.
84 - `absolute {boolean}` - If the pagination links (not the `Link` header) should be absolute or not.<br>Default: `false`.
85 - `paramNames {Object}` - Config object for overriding default parameter names output in the response
86 - `per_page {string}` - Parameter name for describing the page limit <br>Default: `per_page`
87 - `page {string}` - Parameter name for describing the current page <br>Default: `page`
88 - `total {string}` - Parameter name for describing the total item count <br>Default: `total`
89
90#### `reply.bissle(response, [options])`
91
92An additional reply interface for paginated responses.
93- `response {Object}` - The result to be decorated and replied.
94- `options {Object}` - The custom default values.
95 - `key {string}` - The access key of `response` to get the result to be paginated.<br>Default: `'result'`.
96 - `per_page {number}` - The default entries per page if none is defined in the query string.<br>Default: `100`.<br>Range: `1-500`.
97 - `total {number}` - Overwrite the internally generated `total` value and avoid data splicing. The passed response get returned without internally done pagination. Just meta information and the `Link` header get added.<br>Default: `null`.<br>Range: `>=0`.
98
99##Example
100The following example demonstrates the usage of **bissle** in combination with **mongoose**, **halacious** and various utilities.
101
102```js
103const Hapi = require('hapi');
104const bissle = require('bissle');
105const halacious = require('halacious');
106const akaya = require('akaya');
107const Boom = require('boom');
108const _ = require('lodash');
109const YourModel = require('./models/yourModel');
110
111const server = new Hapi.Server();
112server.connection({ port: 1337 });
113
114server.route({
115 method: 'GET',
116 path: '/',
117 config: {
118 id: 'root',
119 handler: function (request, reply) {
120 YourModel.find({}, (err, result) => {
121 if (err) return reply(Boom.badRequest(err));
122 if (!result) return reply(Boom.notFound());
123
124 return reply.bissle({ result });
125 });
126 },
127 plugins: {
128 hal: {
129 prepare: function(rep, next) {
130 _.forEach(rep.entity.result, task => {
131 rep.embed('task', `./${task._id}`, task);
132 });
133
134 return next();
135 },
136 ignore: ['result']
137 }
138 }
139});
140
141server.register([akaya, halacious, {
142 register: bissle,
143 options: { absolute: false }
144}], err => {
145 if (err) throw err;
146
147 server.start();
148});
149```
150
151---
152
153Assuming that **mongoose**'s `find()` returns the following data as `result`:
154
155```js
156[
157 {
158 _id: "abc",
159 title: "abc"
160 },
161 {
162 _id: "def",
163 title: "def"
164 },
165 {
166 _id: "ghi",
167 title: "ghi"
168 },
169 {
170 _id: "jkl",
171 title: "jkl"
172 },
173 {
174 _id: "mno",
175 title: "mno"
176 }
177]
178```
179
180---
181
182Requesting the route `/items?page=2&per_page=2`, the plugin replies:
183
184```js
185{
186 _links: {
187 self: {
188 href: "/items?page=2&per_page=2"
189 },
190 first: {
191 href: "/items?per_page=2"
192 },
193 prev: {
194 href: "/items?per_page=2"
195 },
196 next: {
197 href: "/items?page=3&per_page=2"
198 },
199 last: {
200 href: "/items?page=3&per_page=2"
201 },
202 },
203 page: 2,
204 per_page: 2,
205 total: 5,
206 result: [
207 {
208 _id: "ghi",
209 title: "ghi"
210 },
211 {
212 _id: "jkl",
213 title: "jkl"
214 }
215 ]
216}
217
218```
219
220Additionally the plugin sets the corresponding `Link` header.
221
222---
223
224The **halacious** plugin enables to extend this response to:
225
226```js
227{
228 _links: {
229 self: {
230 href: "/items?page=2&per_page=2"
231 },
232 first: {
233 href: "/items?per_page=2"
234 },
235 prev: {
236 href: "/items?per_page=2"
237 },
238 next: {
239 href: "/items?page=3&per_page=2"
240 },
241 last: {
242 href: "/items?page=3&per_page=2"
243 },
244 },
245 page: 2,
246 per_page: 2,
247 total: 5,
248 _embedded: [
249 {
250 _links: {
251 self: {
252 href: "/items/ghi"
253 }
254 },
255 _id: "ghi",
256 title: "ghi"
257 },
258 {
259 _links: {
260 self: {
261 href: "/items/jkl"
262 }
263 },
264 _id: "jkl",
265 title: "jkl"
266 }
267 ]
268}
269
270```
271
272So in the end the combination of **bissle** and a HAL plugin results in a REST/HAL compliant and paginated response.
273
274## Testing
275First you have to install all dependencies:
276```
277$ npm install
278```
279
280To execute all unit tests once, use:
281```
282$ npm test
283```
284
285or to run tests based on file watcher, use:
286```
287$ npm start
288```
289
290To get information about the test coverage, use:
291```
292$ npm run coverage
293```
294
295## Contribution
296Fork this repository and push in your ideas.
297
298Do not forget to add corresponding tests to keep up 100% test coverage.
299
300## License
301The MIT License
302
303Copyright (c) 2016-2017 Felix Heck
304
305Permission is hereby granted, free of charge, to any person obtaining a copy
306of this software and associated documentation files (the "Software"), to deal
307in the Software without restriction, including without limitation the rights
308to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
309copies of the Software, and to permit persons to whom the Software is
310furnished to do so, subject to the following conditions:
311
312The above copyright notice and this permission notice shall be included in
313all copies or substantial portions of the Software.
314
315THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
316IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
317FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
318AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
319LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
320OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
321THE SOFTWARE.