├── .gitignore ├── LICENCE ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules 3 | *.iml -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nguyễn Kim Kha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # loopback3-xTotalCount 2 | Add `X-Total-Count` header to all search requests for Loopback 3.0. It should be use on client that use [json-server](https://github.com/typicode/json-server), such as [react-admin](https://github.com/marmelab/react-admin) 3 | 4 | You may look for this one [loopback-component-react-admin](https://github.com/kimkha/loopback-component-react-admin): the component on Loopback that help [react-admin](https://github.com/marmelab/react-admin) connect with Loopback API server. 5 | 6 | ## Changes in this fork: 7 | Added compatibility with API calls involving related models. for example: 8 | GET /books/{id}/chapters 9 | Returns the correct count result as `X-Total-Count` header 10 | 11 | ## Install 12 | 13 | ### NPM 14 | 15 | 1. Add `"loopback3-xtotalcount": "latest"` to your `package.json` file. 16 | 2. Run `npm install` OR run `npm install loopback3-xtotalcount` 17 | 3. Set the module in your `component-config.json` (loopback server endpoint) and add `prototype.__get__model-plural-form` for each related model. 18 | 19 | 20 | ```json 21 | "loopback3-xtotalcount": { 22 | "pattern": [ 23 | "*.find" 24 | ], 25 | // More options here 26 | } 27 | ``` 28 | 29 | ### Yarn 30 | 31 | We recommend to use `yarn` instead of `npm`: 32 | 33 | 1. `yarn add loopback3-xtotalcount` 34 | 2. Set the module in your `component-config.json` 35 | 36 | ```json 37 | "loopback3-xtotalcount": { 38 | "pattern": [ 39 | "*.find" 40 | ], 41 | // More options here 42 | } 43 | ``` 44 | 45 | ## Options 46 | 47 | ### `pattern`: Array of String 48 | 49 | Method patterns that `X-Total-Count` header will be added. 50 | 51 | Accepted patterns: See https://loopback.io/doc/en/lb3/Remote-hooks.html#wildcards. 52 | 53 | Default value: `[ "*.find" ]`, which auto added to find method of all models. 54 | 55 | ### `relationMethodNames`: array of String 56 | 57 | In case of sub-model, for example if you want `GET /books/{id}/chapters` to return `X-Total-Count` in header, please add the following: 58 | 59 | ```json 60 | "loopback3-xtotalcount": { 61 | "pattern": [ 62 | "*.find" 63 | ], 64 | "relationMethodNames": [ 65 | "prototype.__get__chapters" 66 | ] 67 | } 68 | ``` 69 | 70 | Rule: Model name is `Chapter`, its plural is `Chapters`, so the URL should be `GET /books/{id}/chapters`, and then the method is `__get__chapters`. That's how it works. 71 | 72 | ## Example 73 | 74 | Please check example here: [loopback-aor-boilerplate](https://github.com/kimkha/loopback-aor-boilerplate), you should clone it and change your model later. 75 | 76 | ## Known issues 77 | 78 | ### Cross-domain header 79 | 80 | By default, loopback doesn't allow expose header over cross-domain. So, if you client site and your loopback server run on 2 different domain, the client won't receive `X-Total-Count` (see [here](https://github.com/kimkha/aor-loopback/issues/2)). 81 | 82 | To fix it, open `middleware.json` and insert following line under `initial.cors.params`: 83 | 84 | ``` 85 | "exposedHeaders": "X-Total-Count" 86 | ``` 87 | 88 | ## License 89 | This module is licensed under the [MIT Licence](LICENSE). 90 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = function (app, options) { 2 | var remotes = app.remotes(); 3 | 4 | // Set X-Total-Count for all search requests 5 | var applyXTotal = function (ctx, next) { 6 | var filter; 7 | if (ctx.args && ctx.args.filter) { 8 | filter = ctx.args.filter.where; 9 | } 10 | 11 | if (!ctx.res._headerSent) { 12 | this.count(filter, function (err, count) { 13 | ctx.res.set('X-Total-Count', count); 14 | next(); 15 | }); 16 | } else { 17 | next(); 18 | } 19 | }; 20 | var pattern = options && Array.isArray(options.pattern) ? options.pattern : ['*.find']; 21 | for (var i=pattern.length-1; i>=0; i--) { 22 | remotes.after(pattern[i], applyXTotal); 23 | } 24 | 25 | // Changes by Thomas Vié 26 | // for nested relational models 27 | var relationMethodNames = options.relationMethodNames; 28 | // Function has to calculate count for nested pattern 29 | // With __count__relatedModelName 30 | 31 | 32 | var models = app.models(); 33 | 34 | for(let model of models) { 35 | for(let relationMethodName of relationMethodNames) { 36 | model.afterRemote(relationMethodName, function (ctx, output, next) { 37 | // Get Model name (final model) 38 | //TODO check if works in all cases 39 | 40 | const relatedModel = app.models[ctx.resultType[0]]; 41 | 42 | var filter; 43 | if (ctx.args && ctx.args.filter) { 44 | filter = ctx.args.filter.where; 45 | } 46 | relatedModel.count(filter, function(err, count) { 47 | if(err) { 48 | throw new Error(err); 49 | } 50 | if (!ctx.res._headerSent) { 51 | ctx.res.set('X-Total-Count', count); 52 | next(); 53 | } else { 54 | throw new Error('Headers already sent !'); 55 | } 56 | }); 57 | }); 58 | } 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "loopback3-xtotalcount", 3 | "version": "0.1.2", 4 | "description": "Add X-Total-Count header to all search requests for Loopback 3.0", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/kimkha/loopback3-xTotalCount.git" 12 | }, 13 | "keywords": [ 14 | "loopback", 15 | "mongodb", 16 | "nodeJs", 17 | "X-Total-Count", 18 | "json-server" 19 | ], 20 | "author": "Nguyen Kim Kha", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/kimkha/loopback3-xTotalCount/issues" 24 | }, 25 | "homepage": "https://github.com/kimkha/loopback3-xTotalCount#README.md" 26 | } 27 | --------------------------------------------------------------------------------