├── .gitignore ├── LICENSE ├── README.md ├── lib └── restpack.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Fred Chien 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 | # restpack 2 | 3 | A library to pack/unpack data for restful APIs, it aims to make developer to implement restful APIs easily. 4 | 5 | ## Installation 6 | You can install it via NPM: 7 | ``` 8 | npm install restpack 9 | ``` 10 | 11 | ## Usage 12 | 13 | RestPack can be used on server-side and client-side both, to make developers to pack/unpack data as restful APIs. 14 | 15 | ### Get Started 16 | 17 | __Server-side__: 18 | ```js 19 | var RestPack = require('restpack'); 20 | 21 | var restpack = new RestPack(); 22 | 23 | // Set data which is sent out to client, RestPack will be packing it 24 | restpack.setData({ 25 | tags: [ 26 | 'Apple', 'Orange' 27 | ] 28 | }); 29 | 30 | // we can send result directly if server is based on Koa 31 | restpack.sendKoa(this); 32 | // response: { tags: [ 'Apple', 'Orange' ] } 33 | 34 | ``` 35 | 36 | If you are using browserify or webpack as build tool, you are able to use RestPack what is just like you do on server-side. 37 | 38 | __Client-side:__ 39 | ```js 40 | var RestPack = require('restpack'); 41 | 42 | // You might access restful API with any kinds of Ajax solutions, then getting HTTP status code and response messages. 43 | var restpack = new RestPack(statusCode, data); 44 | 45 | // Getting data 46 | console.log(restpack.getData()); 47 | ``` 48 | 49 | ### Response For Errors 50 | 51 | Here is an example to show how to make a response from server to client. 52 | 53 | __Server-side__: 54 | ```js 55 | var RestPack = require('restpack'); 56 | 57 | var restpack = new RestPack(); 58 | 59 | // HTTP status code 422 is for Validation faild, and errors we set will be appended to response message. 60 | restpack 61 | .setStatus(RestPack.Status.ValidationFailed) 62 | .appendError('username', RestPack.Code.AlreadyExist) 63 | .appendError('email', RestPack.Code.Invalid) 64 | .appendError('name', RestPack.Code.Required); 65 | 66 | // we can send result directly if server is based on Koa 67 | restpack.sendKoa(this); 68 | /* 69 | status code: 422 70 | response: { 71 | message: 'Validation Failed', 72 | errors: [ 73 | { field: 'username', code: 4 }, 74 | { field: 'email', code: 2 }, 75 | { field: 'name', code: 1 } 76 | ] 77 | } 78 | */ 79 | ``` 80 | 81 | You can use RestPack to get status and error messages by parsing data sent from server. 82 | 83 | __Client-side:__ 84 | ```js 85 | var RestPack = require('restpack'); 86 | 87 | // You might access restful API with any kinds of Ajax solutions, then getting HTTP status code and response messages. 88 | var restpack = new RestPack(statusCode, data); 89 | 90 | // It's validation failed 91 | if (restpack.status == RestPack.Status.ValidationFailed) { 92 | 93 | // Getting all field errors 94 | restpack.errors.forEach(function(err) { 95 | console.log('Field:', err.field); 96 | 97 | // Type of error 98 | switch(err.code) { 99 | RestPack.Code.Required: 100 | console.log('Code: Required'); 101 | RestPack.Code.Invalid: 102 | console.log('Code: Invalid'); 103 | RestPack.Code.NotExist: 104 | console.log('Code: NotExist'); 105 | RestPack.Code.AlreadyExist: 106 | console.log('Code: AlreadyExist'); 107 | } 108 | }); 109 | } 110 | ``` 111 | 112 | ## Status In Common Use 113 | 114 | Several status in common use has been supported already. 115 | ```js 116 | RestPack.Status = { 117 | OK: { status: 200 }, 118 | Created: { status: 201 }, 119 | BadRequest: { status: 400 }, 120 | Forbidden: { status: 403 }, 121 | ServiceExpired: { status: 403, message: 'Service Expired' }, 122 | AccountBlocked: { status: 403, message: 'Account Blocked' }, 123 | PermissionDenied: { status: 403, message: 'Permission Denied' }, 124 | NotFound: { status: 404 }, 125 | ValidationFailed: { status: 422, message: 'Validation Failed' }, 126 | Error: { status: 500 }, 127 | ServiceUnavailable: { status: 503, message: 'Service Unavailable' } 128 | }; 129 | ``` 130 | 131 | ## Errors In Common Use 132 | 133 | Several error code in common use has been supported already. 134 | ```js 135 | RestPack.Code.Required 136 | RestPack.Code.Invalid 137 | RestPack.Code.NotExist 138 | RestPack.Code.AlreadyExist 139 | ``` 140 | 141 | ## License 142 | Licensed under the MIT License 143 | 144 | ## Authors 145 | Copyright(c) 2016 Fred Chien <> 146 | -------------------------------------------------------------------------------- /lib/restpack.js: -------------------------------------------------------------------------------- 1 | 2 | var httpStatus = { 3 | 200: 'OK', 4 | 201: 'Created', 5 | 202: 'Accepted', 6 | 204: 'No Content', 7 | 301: 'Moved Permanently', 8 | 302: 'Found', 9 | 303: 'See Other', 10 | 307: 'Temporary Redirect', 11 | 304: 'Not Modified', 12 | 409: 'Conflict', 13 | 412: 'Precondition Failed', 14 | 400: 'Bad Request', 15 | 401: 'Unauthorizaed', 16 | 403: 'Forbidden', 17 | 404: 'Not Found', 18 | 405: 'Method Not Allowed', 19 | 406: 'Not Acceptable', 20 | 422: 'Unprocessable Entity', 21 | 428: 'Precondition Required', 22 | 500: 'Internal Server Error', 23 | 501: 'Not Implemented', 24 | 503: 'Service Unavailable' 25 | }; 26 | 27 | var RestPack = function(statusCode, data) { 28 | this.status = RestPack.Status.OK; 29 | this.errors = []; 30 | this.data = {}; 31 | 32 | if (data) { 33 | 34 | switch(statusCode) { 35 | case RestPack.Status.OK.status: 36 | case RestPack.Status.Created.status: 37 | this.data = data; 38 | break; 39 | 40 | default: 41 | this.errors = data.errors || []; 42 | 43 | for (var statusSymbol in RestPack.Status) { 44 | var status = RestPack.Status[statusSymbol]; 45 | if (status.status != statusCode) 46 | continue; 47 | 48 | if (status.message == data.message) { 49 | this.status = status; 50 | break; 51 | } 52 | 53 | if (status.default) { 54 | this.status = status; 55 | } 56 | } 57 | } 58 | } 59 | }; 60 | 61 | RestPack.Status = { 62 | OK: { status: 200 }, 63 | Created: { status: 201 }, 64 | BadRequest: { status: 400 }, 65 | Forbidden: { status: 403 }, 66 | ServiceExpired: { status: 403, message: 'Service Expired' }, 67 | AccountBlocked: { status: 403, message: 'Account Blocked' }, 68 | PermissionDenied: { status: 403, message: 'Permission Denied' }, 69 | NotFound: { status: 404, default: true }, 70 | ValidationFailed: { status: 422, message: 'Validation Failed' }, 71 | Error: { status: 500 }, 72 | ServiceUnavailable: { status: 503, message: 'Service Unavailable' } 73 | }; 74 | 75 | RestPack.Code = { 76 | Required: 1, 77 | Invalid: 2, 78 | NotExist: 3, 79 | AlreadyExist: 4 80 | }; 81 | 82 | RestPack.prototype.setStatus = function(status) { 83 | this.status = status; 84 | 85 | return this; 86 | }; 87 | 88 | RestPack.prototype.getStatusCode = function() { 89 | return this.status.status; 90 | }; 91 | 92 | RestPack.prototype.appendError = function(field, code) { 93 | this.errors.push({ 94 | field: field, 95 | code: code 96 | }); 97 | 98 | return this; 99 | }; 100 | 101 | RestPack.prototype.setData = function(data) { 102 | this.data = data; 103 | 104 | return this; 105 | }; 106 | 107 | RestPack.prototype.getData = function() { 108 | 109 | switch(this.status) { 110 | case RestPack.Status.OK: 111 | case RestPack.Status.Created: 112 | return this.data; 113 | } 114 | 115 | return { 116 | message: this.status.message || httpStatus[this.status.status], 117 | errors: this.errors || undefined 118 | }; 119 | }; 120 | 121 | RestPack.prototype.getNormalizedData = function() { 122 | 123 | switch(this.status) { 124 | case RestPack.Status.OK: 125 | case RestPack.Status.Created: 126 | return this.data; 127 | } 128 | 129 | var errors; 130 | if (this.errors) { 131 | errors = []; 132 | for (var index in this.errors) { 133 | var err = this.errors[index]; 134 | 135 | if (err.code) { 136 | 137 | // Clone 138 | var newErr = {}; 139 | for (var key in err) { 140 | newErr[key] = err[key]; 141 | } 142 | 143 | // Using string to replace code 144 | for (var codeName in RestPack.Code) { 145 | if (RestPack.Code[codeName] == newErr.code) { 146 | newErr.code = codeName; 147 | break; 148 | } 149 | } 150 | 151 | errors.push(newErr); 152 | continue; 153 | } 154 | 155 | // Old 156 | errors.push(err); 157 | } 158 | } 159 | 160 | return { 161 | message: this.status.message || httpStatus[this.status.status], 162 | errors: errors || undefined 163 | }; 164 | }; 165 | 166 | RestPack.prototype.sendKoa = function(koa) { 167 | koa.status = this.getStatusCode(); 168 | koa.body = this.getData(); 169 | 170 | return this; 171 | }; 172 | 173 | module.exports = RestPack; 174 | 175 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "restpack", 3 | "version": "0.0.6", 4 | "description": "Library to pack/unpack data for restful API", 5 | "main": "./lib/restpack.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/cfsghost/restpack.git" 12 | }, 13 | "keywords": [ 14 | "restful", 15 | "api" 16 | ], 17 | "author": "Fred Chien ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/cfsghost/restpack/issues" 21 | }, 22 | "homepage": "https://github.com/cfsghost/restpack#readme" 23 | } 24 | --------------------------------------------------------------------------------