├── .gitignore ├── LICENSE ├── README.md ├── example ├── package.json └── server.js ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Yuriy Nemtsov 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Express Partial Response Middleware [![NPM version](https://badge.fury.io/js/express-partial-response.png)](http://badge.fury.io/js/express-partial-response) 2 | 3 | This Express Middleware will allow you to send a subset of a JSON object 4 | instead of the entire object from your HTTP services. To do so, your services 5 | will begin accepting the `?fields=` query-string that, using a simple language, 6 | will specify which fields and sub-fields to keep and which to ignore. 7 | 8 | If you've used the Google APIs, provided a `?fields=` query-string to get a 9 | [Partial Response](https://developers.google.com/+/api/#partial-responses), 10 | and wanted to do the same for your own server, now you can do so with this 11 | middleware. 12 | 13 | _Underneath, this middleware uses [json-mask](https://github.com/nemtsov/json-mask). 14 | Use it directly without this middleware if you need more flexibility._ 15 | 16 | # Installation 17 | 18 | ``` 19 | npm install express-partial-response 20 | ``` 21 | 22 | # Usage 23 | 24 | ```js 25 | const express = require('express'); 26 | const partialResponse = require('express-partial-response'); 27 | const app = express(); 28 | 29 | app.use(partialResponse()); 30 | 31 | app.get('/', (req, res) => { 32 | res.json({ 33 | firstName: 'Mohandas', 34 | lastName: 'Gandhi', 35 | aliases: [ 36 | { 37 | firstName: 'Mahatma', 38 | lastName: 'Gandhi' 39 | }, 40 | { 41 | firstName: 'Bapu' 42 | } 43 | ] 44 | }); 45 | }); 46 | 47 | app.listen(4000); 48 | ``` 49 | 50 | Let's test it: 51 | 52 | ``` 53 | $ curl 'http://localhost:4000' 54 | {"firstName":"Mohandas","lastName":"Gandhi","aliases":[{"firstName":"Mahatma","lastName":"Gandhi"},{"firstName":"Bapu"}]} 55 | 56 | $ # Let's just get the first name 57 | $ curl 'http://localhost:4000?fields=lastName' 58 | {"lastName":"Gandhi"} 59 | 60 | $ # Now, let's just get the first names directly as well as from aliases 61 | $ curl 'http://localhost:4000?fields=firstName,aliases(firstName)' 62 | {"firstName":"Mohandas","aliases":[{"firstName":"Mahatma"},{"firstName":"Bapu"}]} 63 | ``` 64 | 65 | **Note:** take a look at `/example`. 66 | 67 | # Syntax 68 | 69 | Look at [json-mask](https://github.com/nemtsov/json-mask) for the available syntax of the `fields` param. 70 | 71 | # Options 72 | 73 | `query` specifies the query-string to use. Defaults to `fields` 74 | 75 | ```js 76 | app.use( 77 | partialResponse({ 78 | query: 'filter' 79 | }) 80 | ); 81 | ``` 82 | 83 | # License 84 | 85 | MIT. See LICENSE 86 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "server.js", 5 | "scripts": { 6 | "start": "node server.js" 7 | }, 8 | "dependencies": { 9 | "express": "^4.17.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const partialResponse = require('../'); 3 | const app = express(); 4 | 5 | app.use(partialResponse()); 6 | 7 | app.get('/', (req, res) => { 8 | res.json({ 9 | firstName: 'Mohandas', 10 | lastName: 'Gandhi', 11 | aliases: [ 12 | { 13 | firstName: 'Mahatma', 14 | lastName: 'Gandhi' 15 | }, 16 | { 17 | firstName: 'Bapu' 18 | } 19 | ] 20 | }); 21 | }); 22 | 23 | app.listen(4000, () => { 24 | const prefix = "curl 'http://localhost:4000?fields=%s'"; 25 | console.log('Server running on :4000, try the following:'); 26 | console.log(prefix, '*'); 27 | console.log(prefix, 'lastName'); 28 | console.log(prefix, 'firstName,aliases(firstName)'); 29 | }); 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const jsonMask = require('json-mask'); 2 | 3 | function isBadCode(statusCode) { 4 | return statusCode < 200 || statusCode >= 300; 5 | } 6 | 7 | module.exports = function(options = {}) { 8 | options = { query: 'fields', ...options }; 9 | 10 | function wrap(resJson) { 11 | return function(obj) { 12 | const fields = this.req.query[options.query]; 13 | 14 | // deprecated API that are still supported in v4 15 | if (arguments.length > 1) { 16 | // res.json(obj, status) 17 | if ('number' === typeof arguments[1] && !isBadCode(arguments[1])) { 18 | return resJson(jsonMask(obj, fields), arguments[1]); 19 | } 20 | 21 | // res.json(status, obj) 22 | if ('number' === typeof obj && !isBadCode(obj)) { 23 | return resJson(obj, jsonMask(arguments[1], fields)); 24 | } 25 | 26 | return resJson(...arguments); 27 | } 28 | 29 | return isBadCode(this.statusCode) 30 | ? resJson(...arguments) 31 | : resJson(jsonMask(obj, fields)); 32 | }; 33 | } 34 | 35 | return function(req, res, next) { 36 | if (!res.__isJSONMaskWrapped) { 37 | res.json = wrap(res.json.bind(res)); 38 | if (req.jsonp) res.jsonp = wrap(res.jsonp.bind(res)); 39 | res.__isJSONMaskWrapped = true; 40 | } 41 | next(); 42 | }; 43 | }; 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-partial-response", 3 | "version": "1.0.4", 4 | "description": "Express middleware for filtering-out parts of JSON responses based on the `fields` query-string.", 5 | "dependencies": { 6 | "json-mask": "^1.0.4" 7 | }, 8 | "main": "index.js", 9 | "keywords": [ 10 | "express", 11 | "partial-response", 12 | "filter", 13 | "mask", 14 | "select", 15 | "fields", 16 | "projection", 17 | "query", 18 | "json" 19 | ], 20 | "engines": { 21 | "node": ">=10" 22 | }, 23 | "husky": { 24 | "hooks": { 25 | "pre-commit": "pretty-quick --staged" 26 | } 27 | }, 28 | "prettier": { 29 | "singleQuote": true 30 | }, 31 | "author": "nemtsov@gmail.com", 32 | "license": "MIT", 33 | "repository": { 34 | "type": "git", 35 | "url": "git://github.com/nemtsov/express-partial-response.git" 36 | }, 37 | "devDependencies": { 38 | "husky": "^4.3.0", 39 | "prettier": "^2.1.2", 40 | "pretty-quick": "^3.1.0" 41 | } 42 | } 43 | --------------------------------------------------------------------------------