├── .gitignore ├── .jshintrc ├── README.md ├── _config.yml ├── app └── app-generator.js ├── default.js ├── helpers ├── apiRoutes.js ├── logger.js └── response.js ├── index.js ├── logs └── placeholder.txt ├── package.json ├── routing └── app-router.js └── settings └── express.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /logs/*json -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": false, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "esnext": true, 7 | "latedef": true, 8 | "noarg": true, 9 | "node": true, 10 | "strict": true, 11 | "undef": true, 12 | "unused": false, 13 | "globals": { 14 | "requre": true 15 | } 16 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Express Logo](https://i.cloudup.com/zfY6lL7eFa-3000x3000.png)](http://expressjs.com/) 3 | 4 | ### Generate NodeJs Express App 5 | 6 | #### Installation - 7 | 8 | `` 9 | npm install express-app-generator --save 10 | `` 11 | 12 | To make node app with express framework, many things are needed i.e - 13 | - Environment where the server is made. 14 | - Generic Responses are set, in which form their is need. 15 | - Routing Generic need to set. 16 | - Logging where ther is a need. 17 | 18 | Now what this Module Provide to us - 19 | - An Enviroment where server is Run on Any Perticular Port. 20 | - A Generic Router with unlimited middleware can be set. 21 | - Give Generic Response as Data ,Items etc. 22 | - Obvisously a Logger which helps to Log things. 23 | 24 | The generated app Provide us some default express Features like - 25 | - `bodyParser.json();` 26 | - `bodyParser.urlencoded({extended: true});` 27 | - `bodyParser({ limit: '50mb', keepExtensions: true })` 28 | - `'view engine', 'ejs'` 29 | - express static path `public` folder in root of app folder 30 | - if port is null it use default port `3000` 31 | 32 | 33 | ## Generate Express App 34 | ```sh 35 | let appGenrator=require('express-app-generator'); 36 | appGenrator.generate(3789, 'src/api', function(err,app){ 37 | if(err){ 38 | return console.log(err); 39 | } 40 | // your express app is Ready to use 41 | //you can check this on http://localhost:3789 42 | console.log(app) 43 | }); 44 | ``` 45 | 46 | If you want to change express properties , 47 | you can change it by update `app.use()` or `app.set()`; 48 | 49 | ## Routing 50 | - Clean and Easy To Use. 51 | - **Required Things** 52 | 53 | > Provide the API path (where all the API's are kept) from the root of APP. 54 | This will be string like `'src/api' and 55 | in this `api` folder all the .js files will be like 56 | `users.js` not `user.js` , `employees.js` not `employee.js` 57 | which means `s` or `es` sufix is must. 58 | 59 | > The `logs` folder is required ,this folder is use for 60 | logging the logs in json files ,which we get as in console 61 | 62 | 63 | - This Router Provides Simple CRUD Mechanism . In which Combinations of Arrays and Objects are use To Build it. 64 | - `appRouter` method which is present in app generator helps to make Routing in Flexible way. 65 | 66 | *Lets get started for Routing* - 67 | **REST or CRUD** 68 | ```sh 69 | let api= app.appRouter; 70 | //app is Express genrated app (above define how to get it) 71 | api.model('users') 72 | .register('REST', [ authentication ]); 73 | ``` 74 | This will make URL as http://localhost:3789/api/users . 75 | 76 | We can use `REST` or `CRUD` Keyword to get `create,update,delete,get,search` these 77 | methods ready to use but for this, 78 | You must have all these methods present in `/api/users.js` file . 79 | 80 | The array which is present just after the `REST` is use for middlewares. 81 | Suppose I want to make authentication every time so the function of 82 | authentication will be pass in the middleware Array. 83 | 84 | Things `REST` Or `CRUD` Gives - 85 | 86 | - If the Request is for **`POST`** then it go for `expots.create=(req,res)=>{};` 87 | 88 | - If the Request is for **`GET`** then it go for `expots.get=(req,res)=>{};` 89 | http://localhost:3789/api/users/:id 90 | 91 | - If the Request is for **`GET`** then it go for `expots.search=(req,res)=>{};` 92 | 93 | http://localhost:3789/api/users 94 | Here many query params `?` can use. 95 | 96 | - If the Request is for **`PUT`** then it go for `expots.update=(req,res)=>{};` 97 | 98 | - If the Request is for **`DELETE`** then it go for `expots.delete=(req,res)=>{};` 99 | 100 | 101 | **Note** - All the the Middleware fuctions must have parameters `req,res,next` where 102 | req,res are request response express generated objects and next is the 103 | callback function which will call when one middleware work is DONE and 104 | it will go to next middleware function (if present) in that array and perform same, 105 | then go the main function in the `users.js`. 106 | 107 | **User Define Requests** - 108 | 109 | - In register `action` and `method` is Requied 110 | - fiter for One Middleware and filters for Multiple 111 | - For Single Request - 112 | ```sh 113 | api.model('users') 114 | .register({ 115 | action: 'GET', 116 | method: 'get', // method must present inside users.js 117 | url: '/:id', 118 | filters: [authentication] //middlewares 119 | }); 120 | 121 | // URL will be - 122 | // http://localhost:3789/api/users/:id 123 | ``` 124 | 125 | 126 | - For Multiple Requests - 127 | 128 | ```sh 129 | api.model('users') 130 | .register([{ 131 | action: 'POST', 132 | method: 'create' 133 | },{ 134 | action: 'POST', 135 | method: 'createWithProfile', 136 | url: '/withProfile', 137 | filter: authentication 138 | }, { 139 | action: 'POST', 140 | method: 'importer', 141 | url: '/importer', 142 | filter: authentication 143 | }, { 144 | action: 'POST', 145 | method: 'createWhenSignIn', 146 | filter: authentication 147 | }, { 148 | action: 'PUT', 149 | method: 'update', 150 | url: '/:id', 151 | filters: [authentication, groupCode] 152 | }, { 153 | action: 'GET', 154 | method: 'get', 155 | url: '/:id', 156 | filters: [authentication, groupCode] 157 | }, { 158 | action: 'GET', 159 | method: 'search', 160 | filters: [authentication, groupCode] 161 | }, { 162 | action: 'GET', 163 | method: 'searchInGroup', 164 | url: '/from/group', 165 | filters: [authentication, groupCode] 166 | }, { 167 | action: 'GET', 168 | method: 'getLeader', 169 | url: '/get/leader/:id', 170 | filters: [authentication, groupCode] 171 | }]); 172 | ``` 173 | 174 | **Note**: `api` keyword in URL is Constant all over the Router. 175 | So your base url will be http://localhost:3789/api/ . 176 | 177 | ## Responses 178 | 179 | Their are Four Types of Responses 180 | All the Responses is of `status Code is 200` . 181 | - data 182 | - It can take one or two params `data,message`, 183 | `data` is mandatory. 184 | - It is use as - 185 | 186 | ```sh 187 | res.data({ 188 | name:"Leo", 189 | age:21 190 | }); 191 | 192 | //res which you get from any query 193 | ``` 194 | 195 | - This will give Response - 196 | ```sh 197 | { 198 | "isSuccess":true, 199 | "data":{ 200 | "name":"Leo", 201 | "age":21 202 | } 203 | } 204 | ``` 205 | - page 206 | - It can take one or two params `items, pageSize, pageNo, totalRecordsCount` 207 | `items` is mandatory. 208 | - Can also use for Paginations. 209 | - It is use as - 210 | 211 | ```sh 212 | res.page([{ 213 | name:"Leo", 214 | age:21 215 | },{ 216 | name:"Hardy", 217 | age:22 218 | }]); 219 | 220 | //res which you get from any query 221 | ``` 222 | 223 | - This will give Response - 224 | ```sh 225 | { 226 | "isSuccess":true, 227 | "items":[{ 228 | "name":"Leo", 229 | "age":21 230 | },{ 231 | "name":"Hardy", 232 | "age":22 233 | }], 234 | pageSize:2 235 | } 236 | ``` 237 | - success 238 | - It can takes one params `message`, it is mandatory. 239 | - Can Use for Sending Successful Signature message. 240 | - It is use as - 241 | 242 | ```sh 243 | res.success(`user successfully deleted`); 244 | ``` 245 | 246 | - This will give Response - 247 | ```sh 248 | { 249 | "isSuccess":true, 250 | "message":"user successfully deleted" 251 | } 252 | ``` 253 | - failure 254 | - It can takes two params `error, message`, 255 | `error` it is mandatory. 256 | - Can Use for Sending Error Signature message. 257 | - It is use as - 258 | 259 | ```sh 260 | res.failure(`user Hardy already present`); 261 | ``` 262 | 263 | - This will give Response - 264 | ```sh 265 | { 266 | "isSuccess":false, 267 | "message":"user Hardy already present" 268 | } 269 | ``` 270 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /app/app-generator.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var logger = require('../helpers/logger')('app'), 3 | appRouter = require('../routing/app-router'), 4 | express = require('express'), 5 | app = express(); 6 | 7 | require('../settings/express').configure(app); 8 | 9 | exports.generator = (listenOn, apiFullFolderPath, cb) => { 10 | 11 | 12 | if (listenOn && !cb) { 13 | cb = listenOn; 14 | } 15 | var port = listenOn || process.env.PORT || 3000, 16 | server = app.listen(port, function() { 17 | logger.info('listening on ' + port); 18 | app.appRouter = appRouter.configure(app, apiFullFolderPath); 19 | cb(null, app, server); 20 | }); 21 | }; -------------------------------------------------------------------------------- /default.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.logger = { 3 | "file": { 4 | "filename": "logs/logs.json", 5 | "level": "silly", 6 | "handleExceptions": true, 7 | "json": true, 8 | "maxsize": 512000, //0.5 9 | "maxFiles": 5, 10 | "colorize": false 11 | }, 12 | "console": { 13 | "level": "silly", 14 | "handleExceptions": true, 15 | "json": false, 16 | "colorize": true 17 | } 18 | }; -------------------------------------------------------------------------------- /helpers/apiRoutes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('underscore'); 3 | var responseHelper = require('./response'); 4 | var logger = require('../helpers/logger')(); 5 | var async = require('async'); 6 | var appRoot = require('app-root-path'); 7 | var join = require('path').join; 8 | 9 | var responseDecoratorFn = function(req, res, next) { 10 | res.log = logger.start(req.method + ' ' + req.url); 11 | if (req.body) { 12 | res.log.debug(req.body); 13 | } 14 | var wrapper = responseHelper(res); 15 | res.failure = wrapper.failure; 16 | res.success = wrapper.success; 17 | res.page = wrapper.page; 18 | res.data = wrapper.data; 19 | next(); 20 | }; 21 | 22 | 23 | module.exports = function(app, apiFullFolderPath) { 24 | 25 | let apiRoot, picApiFrom, requiredModule; 26 | var tasks = []; 27 | 28 | let register = function(option, filters) { 29 | 30 | if (_.isEmpty(requiredModule)) { 31 | return; 32 | } 33 | 34 | tasks.push(responseDecoratorFn); 35 | 36 | if (typeof(option) === "string" && option.toUpperCase() === 'REST' || 37 | typeof(option) === "string" && option.toUpperCase() === 'CRUD') { 38 | 39 | if (filters) { 40 | if (Array.isArray(filters)) { 41 | filters.forEach(item => tasks.push(item)); 42 | } else { 43 | tasks.push(filters); 44 | } 45 | } 46 | 47 | (function() { 48 | let apiUrl = apiRoot + '/:id'; 49 | 50 | if (requiredModule.get) { 51 | tasks.push(requiredModule.get); 52 | app.get(apiUrl, tasks); 53 | tasks.pop(); 54 | } 55 | if (requiredModule.search) { 56 | tasks.push(requiredModule.search); 57 | app.get(apiRoot, tasks); 58 | tasks.pop(); 59 | } 60 | if (requiredModule.update) { 61 | tasks.push(requiredModule.update); 62 | app.put(apiUrl, tasks); 63 | tasks.pop(); 64 | } 65 | if (requiredModule.create) { 66 | tasks.push(requiredModule.create); 67 | app.post(apiRoot, tasks); 68 | tasks.pop(); 69 | } 70 | if (requiredModule.delete) { 71 | tasks.push(requiredModule.delete); 72 | app.delete(apiUrl, tasks); 73 | tasks.pop(); 74 | } 75 | if (requiredModule.patch) { 76 | tasks.push(requiredModule.patch); 77 | app.patch(apiUrl, tasks); 78 | tasks.pop(); 79 | } 80 | 81 | })(); 82 | } 83 | 84 | if (typeof(option) === "object" && !filters) { //come as array or object 85 | var options = []; 86 | 87 | if (option[0]) { 88 | options = option; 89 | } else { 90 | options.push(option); 91 | } 92 | 93 | options.forEach(function(item) { 94 | let filters = []; 95 | 96 | filters = item.filters ? item.filters : []; 97 | 98 | if (item.filter) { 99 | filters.push(item.filter); 100 | } 101 | 102 | filters.forEach(item => tasks.push(item)); 103 | 104 | tasks.push(requiredModule[item.method]); 105 | 106 | let apiUrl = item.url ? apiRoot + item.url : apiRoot; 107 | 108 | switch (item.action.toUpperCase()) { 109 | case "GET": 110 | app.get(apiUrl, tasks); 111 | tasks.splice(1, filters.length + 1); 112 | break; 113 | 114 | case "POST": 115 | app.post(apiUrl, tasks); 116 | tasks.splice(1, filters.length + 1); 117 | break; 118 | 119 | case "PUT": 120 | app.put(apiUrl, tasks); 121 | tasks.splice(1, filters.length + 1); 122 | break; 123 | 124 | case "DELETE": 125 | app.delete(apiUrl, tasks); 126 | tasks.splice(1, filters.length + 1); 127 | break; 128 | 129 | case "PATCH": 130 | app.patch(apiUrl, tasks); 131 | tasks.splice(1, filters.length + 1); 132 | break; 133 | 134 | default: 135 | app[item.action.toLocaleLowerCase()](apiUrl, tasks); 136 | tasks.splice(1, filters.length + 1); 137 | break; 138 | } 139 | 140 | }); 141 | } 142 | tasks = []; 143 | return; 144 | 145 | }; 146 | 147 | return { 148 | model: function(apiType) { 149 | 150 | if (apiType.charAt(apiType.length - 1) !== 's' && 151 | apiType.substr(apiType.length - 2, apiType.length) !== 'es') { 152 | throw ('enter correct api'); 153 | } 154 | 155 | picApiFrom = join(appRoot.path, apiFullFolderPath, apiType); 156 | apiRoot = '/api/' + apiType; 157 | requiredModule = require(`${picApiFrom}`); 158 | return { register: register }; 159 | } 160 | }; 161 | }; -------------------------------------------------------------------------------- /helpers/logger.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var winston = require('winston'); 4 | var logConfig = require('../default').logger; 5 | 6 | var transports = []; 7 | 8 | if (logConfig.file) { 9 | transports.push(new winston.transports.File(logConfig.file)); 10 | } 11 | 12 | if (logConfig.console) { 13 | transports.push(new winston.transports.Console(logConfig.console)); 14 | } 15 | 16 | if (logConfig.http) { 17 | transports.push(new winston.transports.Http(logConfig.http)); 18 | } 19 | 20 | var defaultLogger = new winston.Logger({ 21 | transports: transports, 22 | exitOnError: false 23 | }); 24 | 25 | defaultLogger.stream = { 26 | write: function(message, encoding) { 27 | defaultLogger.info(message); 28 | } 29 | }; 30 | 31 | 32 | module.exports = function(ctx) { 33 | 34 | var logger = new winston.Logger({ 35 | transports: transports, 36 | exitOnError: false 37 | }); 38 | 39 | var stringifiedCtx = function(param) { 40 | if (ctx) { 41 | return '[' + ctx + (param ? ':' + param : '') + '] '; 42 | } else if (param) { 43 | return '[' + param + '] '; 44 | } else { 45 | return ''; 46 | } 47 | }; 48 | 49 | var insertCtx = function(params, additional) { 50 | if (typeof params[0] === 'string') { 51 | params[0] = stringifiedCtx(additional) + params[0]; 52 | } else if (typeof params[0] === 'object') { 53 | Array.prototype.unshift.call(params, stringifiedCtx(additional)); 54 | } 55 | 56 | return params; 57 | }; 58 | 59 | var decorator = function(param) { 60 | return { 61 | error: function() { 62 | logger.error.apply(this, insertCtx(arguments, param)); 63 | }, 64 | warn: function() { 65 | logger.warn.apply(this, insertCtx(arguments, param)); 66 | }, 67 | info: function() { 68 | logger.info.apply(this, insertCtx(arguments, param)); 69 | }, 70 | verbose: function() { 71 | logger.varbose.apply(this, insertCtx(arguments, param)); 72 | }, 73 | debug: function() { 74 | logger.debug.apply(this, insertCtx(arguments, param)); 75 | }, 76 | silly: function() { 77 | logger.silly.apply(this, insertCtx(arguments, param)); 78 | } 79 | }; 80 | }; 81 | 82 | var decoratorObj = decorator(); 83 | 84 | decoratorObj.start = function(param) { 85 | var wrapper = decorator(param); 86 | 87 | wrapper.debug('started'); 88 | return wrapper; 89 | }; 90 | 91 | 92 | return decoratorObj; 93 | }; -------------------------------------------------------------------------------- /helpers/response.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = function(res) { 3 | return { 4 | success: function(message) { 5 | let val = { 6 | isSuccess: true, 7 | message: message 8 | }; 9 | res.log.info(message || 'success', val); 10 | res.json(val); 11 | }, 12 | failure: function(error) { 13 | let val = { 14 | isSuccess: false, 15 | error: (typeof error === 'string') ? error : { message: error.message, stack: error.stack }, 16 | }; 17 | res.log.error('failed', val); 18 | res.json(val); 19 | }, 20 | data: function(item) { 21 | let val = { 22 | isSuccess: true, 23 | data: item 24 | }; 25 | res.log.info('success', val); 26 | res.json(val); 27 | }, 28 | page: function(items, total, pageNo, totalRecordsCount) { 29 | let val = { 30 | isSuccess: true, 31 | pageNo: pageNo || 1, 32 | items: items, 33 | pageSize: total || items.length, 34 | totalRecords: totalRecordsCount 35 | }; 36 | 37 | res.log.info('page', val); 38 | res.json(val); 39 | } 40 | }; 41 | }; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | let appKit = require('./app/app-generator'); 3 | 4 | exports.generate = function(port, apiFullFolderPath, cb) { 5 | appKit.generator(port, apiFullFolderPath, (err, app, server) => { 6 | if (err) { 7 | return cb(err); 8 | } 9 | return cb(null, app, server); 10 | }); 11 | }; 12 | 13 | // exports.generate(3211, function(err, app) { 14 | // if (err) { 15 | // throw err; 16 | // } 17 | // // console.log(app); 18 | // }); -------------------------------------------------------------------------------- /logs/placeholder.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hardeep-bit/express-app-generator/a16848f6266c0215f3c04e5d984084e3b0b315dc/logs/placeholder.txt -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-app-generator", 3 | "version": "1.0.6", 4 | "description": "Genrate node app with express with simple routing", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/hardy12994/express-app-generator" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "author": "Hardeep Singh ", 14 | "license": "ISC", 15 | "dependencies": { 16 | "app-root-path": "^2.0.1", 17 | "body-parser": "^1.17.2", 18 | "ejs": "^2.5.6", 19 | "express": "^4.15.3", 20 | "path": "^0.12.7", 21 | "underscore": "^1.8.3", 22 | "winston": "^2.3.1" 23 | } 24 | } -------------------------------------------------------------------------------- /routing/app-router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var apiRoutes = require('../helpers/apiRoutes'); 3 | var fs = require('fs'); 4 | var appRoot = require('app-root-path'); 5 | var loggerConfig = require('../default').logger; 6 | 7 | 8 | module.exports.configure = (app, apiFullFolderPath) => { 9 | app.get('/', (req, res) => { 10 | res.writeHeader(200, { "Content-Type": "text/html" }); 11 | res.write('~ Node Express Genrator Working ~'); 12 | res.end(); 13 | }); 14 | 15 | var api = apiRoutes(app, apiFullFolderPath); 16 | return api; 17 | }; -------------------------------------------------------------------------------- /settings/express.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var express = require('express'); 3 | var path = require('path'); 4 | var bodyParser = require('body-parser'); 5 | var appRoot = require('app-root-path'); 6 | 7 | exports.configure = function(app) { 8 | app.use(bodyParser.json()); 9 | app.use(bodyParser.urlencoded({ 10 | extended: true 11 | })); 12 | app.use(express.static(path.join(appRoot.path, 'public'))); 13 | app.set('view engine', 'ejs'); 14 | app.use(bodyParser({ limit: '50mb', keepExtensions: true })); 15 | }; --------------------------------------------------------------------------------