├── .cfignore ├── .eslintignore ├── test-e2e ├── .eslintrc └── app.spec.js ├── .eslintrc ├── .babelrc ├── config ├── test.js ├── production.js └── default.js ├── manifest.yml ├── src ├── common │ ├── logger.js │ └── loadRoutes.js ├── routes.js ├── controllers │ └── TestController.js ├── bootstrap.js ├── services │ └── TestService.js └── app.js ├── test ├── .eslintrc ├── services │ └── TestService.spec.js └── setup.js ├── .github └── ISSUE_TEMPLATE.md ├── postman ├── bluemix starter.postman_environment.json └── Bluemix starter.postman_collection.json ├── swagger.yaml ├── .gitignore ├── README.md └── package.json /.cfignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/** 2 | node_modules/** -------------------------------------------------------------------------------- /test-e2e/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends" : "../test/.eslintrc" 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-topcoder/nodejs-babel" 3 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"], 3 | "plugins": ["transform-runtime"] 4 | } -------------------------------------------------------------------------------- /config/test.js: -------------------------------------------------------------------------------- 1 | /* eslint import/no-commonjs: 0 */ 2 | 3 | module.exports = { 4 | VERBOSE_LOGGING: false, 5 | }; 6 | -------------------------------------------------------------------------------- /config/production.js: -------------------------------------------------------------------------------- 1 | /* eslint import/no-commonjs: 0 */ 2 | 3 | module.exports = { 4 | VERBOSE_LOGGING: false, 5 | }; 6 | -------------------------------------------------------------------------------- /config/default.js: -------------------------------------------------------------------------------- 1 | /* eslint import/no-commonjs: 0, no-magic-numbers: 0 */ 2 | 3 | module.exports = { 4 | PORT: process.env.PORT || 3200, 5 | VERBOSE_LOGGING: true, 6 | }; 7 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | applications: 2 | - path: . 3 | memory: 256M 4 | instances: 1 5 | domain: eu-gb.mybluemix.net 6 | name: sky-bluemix-starter-2 7 | host: sky-bluemix-starter-2 8 | disk_quota: 1024M 9 | -------------------------------------------------------------------------------- /src/common/logger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Shared bunyan instance 3 | */ 4 | 5 | import bunyan from 'bunyan'; 6 | 7 | const logger = bunyan.createLogger({name: 'app', serializers: bunyan.stdSerializers}); 8 | 9 | export default logger; 10 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends" : "../.eslintrc", 3 | "env" : { 4 | "mocha" : true 5 | }, 6 | "globals": { 7 | "expect": true, 8 | }, 9 | "rules": { 10 | "no-unused-expressions": 0, 11 | "no-magic-numbers": 0 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains all application endpoints 3 | */ 4 | 5 | import TestController from './controllers/TestController'; 6 | 7 | export default { 8 | '/test': { 9 | get: { 10 | method: TestController.testMethod, 11 | public: true, 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /test/services/TestService.spec.js: -------------------------------------------------------------------------------- 1 | import TestService from '../../src/services/TestService'; 2 | 3 | describe('MiscService', () => { 4 | describe('#testMethod', () => { 5 | it('should return success', async() => { 6 | const result = await TestService.testMethod(); 7 | expect(result).to.deep.equal({success: true}); 8 | }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description: 2 | 3 | ### Requirements: 4 | 5 | ### Setup & Reference: 6 | 7 | ### Submissions: 8 | - Ensure good test coverage on all modules 9 | - Upload documentation for how to run your submission 10 | - Upload all your source code as a zip for review 11 | - Winner will be required to submit a pull request with their winning code. 12 | -------------------------------------------------------------------------------- /test-e2e/app.spec.js: -------------------------------------------------------------------------------- 1 | import supertest from 'supertest'; 2 | import app from '../src/app'; 3 | 4 | describe('E2E tests', () => { 5 | let request; 6 | 7 | before(() => { 8 | request = supertest(app); 9 | }); 10 | 11 | it('should return success in GET /test', (done) => { 12 | request.get('/api/test').expect(200, {success: true}, done); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/controllers/TestController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample controller 3 | */ 4 | import TestService from '../services/TestService'; 5 | 6 | 7 | export default { 8 | testMethod, 9 | }; 10 | 11 | /** 12 | * Get test 13 | * @param {Object} req 14 | * @param {Object} res 15 | */ 16 | async function testMethod(req, res) { 17 | const result = await TestService.testMethod(); 18 | res.json(result); 19 | } 20 | -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configure all libraries 3 | * Include this module as a main entry point 4 | */ 5 | 6 | import bluebird from 'bluebird'; 7 | import decorate from 'decorate-it'; 8 | import config from 'config'; 9 | 10 | require('babel-runtime/core-js/promise').default = bluebird; // eslint-disable-line import/no-commonjs 11 | 12 | decorate.configure({ 13 | debug: config.VERBOSE_LOGGING, 14 | }); 15 | -------------------------------------------------------------------------------- /postman/bluemix starter.postman_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2a1d091f-39db-7bbb-27f8-7f0679a64805", 3 | "name": "bluemix starter", 4 | "values": [ 5 | { 6 | "key": "URL", 7 | "value": "http://tc-bluemix-starter.eu-gb.mybluemix.net/api", 8 | "type": "text", 9 | "enabled": true 10 | } 11 | ], 12 | "timestamp": 1482319942110, 13 | "_postman_variable_scope": "environment", 14 | "_postman_exported_at": "2016-12-21T11:33:32.405Z", 15 | "_postman_exported_using": "Postman/4.9.2" 16 | } -------------------------------------------------------------------------------- /swagger.yaml: -------------------------------------------------------------------------------- 1 | swagger: '2.0' 2 | 3 | info: 4 | version: "0.0.1" 5 | title: Blue mix starter 6 | 7 | basePath: /api 8 | schemes: 9 | - http 10 | consumes: 11 | - application/json 12 | produces: 13 | - application/json 14 | 15 | paths: 16 | /test: 17 | get: 18 | description: Test method 19 | 20 | responses: 21 | 200: 22 | description: successful operation 23 | schema: 24 | type: object 25 | properties: 26 | success: 27 | type: boolean 28 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Configure testing utils 3 | */ 4 | 5 | import chai from 'chai'; 6 | import supertest from 'supertest'; 7 | import sinonChai from 'sinon-chai'; 8 | import decorate from 'decorate-it'; 9 | import chaiAsPromised from 'chai-as-promised'; 10 | import '../src/bootstrap'; 11 | 12 | chai.use(sinonChai); 13 | chai.use(chaiAsPromised); 14 | 15 | global.chai = chai; 16 | global.expect = chai.expect; 17 | global.should = chai.should(); 18 | 19 | Promise.promisifyAll(supertest); 20 | 21 | decorate.configure({ 22 | debug: false, 23 | }); 24 | -------------------------------------------------------------------------------- /src/services/TestService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample service 3 | */ 4 | import decorate from 'decorate-it'; 5 | 6 | // ------------------------------------ 7 | // Exports 8 | // ------------------------------------ 9 | 10 | const TestService = { 11 | testMethod, 12 | }; 13 | 14 | decorate(TestService, 'TestService'); 15 | 16 | export default TestService; 17 | 18 | // ------------------------------------ 19 | // Public 20 | // ------------------------------------ 21 | 22 | /** 23 | * Test method 24 | * @returns {{success: Boolean}} the test result 25 | */ 26 | async function testMethod() { 27 | return await Promise.resolve({success: true}); 28 | } 29 | 30 | testMethod.schema = { 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Node template 2 | 3 | config/local.json 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | dist 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (http://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directories 34 | node_modules 35 | jspm_packages 36 | 37 | # Optional npm cache directory 38 | .npm 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | -------------------------------------------------------------------------------- /postman/Bluemix starter.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "17998025-8efe-86b4-bad2-930d50480ead", 3 | "name": "Bluemix starter", 4 | "description": "", 5 | "order": [ 6 | "81da9d25-4861-5890-78bb-2e3f9838213b" 7 | ], 8 | "folders": [], 9 | "timestamp": 1482319894033, 10 | "owner": "62523", 11 | "public": false, 12 | "requests": [ 13 | { 14 | "id": "81da9d25-4861-5890-78bb-2e3f9838213b", 15 | "headers": "", 16 | "url": "{{URL}}/test", 17 | "pathVariables": {}, 18 | "preRequestScript": null, 19 | "method": "GET", 20 | "collectionId": "17998025-8efe-86b4-bad2-930d50480ead", 21 | "data": null, 22 | "dataMode": "params", 23 | "name": "Test method", 24 | "description": "", 25 | "descriptionFormat": "html", 26 | "time": 1482319969191, 27 | "version": 2, 28 | "responses": [], 29 | "tests": null, 30 | "currentHelper": "normal", 31 | "helperAttributes": {} 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /src/common/loadRoutes.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import HTTPError from 'http-errors'; 3 | import wrapAsync from 'express-wrap-async'; 4 | 5 | /** 6 | * Load routes and apply authentication middleware 7 | * @param {Object} router the express router 8 | * @param {Object} routes the route config 9 | */ 10 | export default function loadRoutes(router, routes) { 11 | _.forEach(routes, (verbs, url) => { 12 | _.forEach(verbs, (def, verb) => { 13 | const actions = [ 14 | (req, res, next) => { 15 | if (def.public) { 16 | next(); 17 | return; 18 | } 19 | if (!req.user) { 20 | next(new HTTPError.Unauthorized()); 21 | return; 22 | } 23 | next(); 24 | }, 25 | ]; 26 | const method = def.method; 27 | if (!method) { 28 | throw new Error(`method is undefined in ${verb.toUpperCase()} ${url}`); 29 | } 30 | actions.push(method); 31 | router[verb](url, wrapAsync(actions)); 32 | }); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The application entry point 3 | */ 4 | 5 | import express from 'express'; 6 | import cors from 'cors'; 7 | import bodyParser from 'body-parser'; 8 | import domainMiddleware from 'express-domain-middleware'; 9 | import {errorHandler, notFoundHandler} from 'express-api-error-handler'; 10 | import config from 'config'; 11 | import './bootstrap'; 12 | import routes from './routes'; 13 | import loadRoutes from './common/loadRoutes'; 14 | import logger from './common/logger'; 15 | 16 | const app = express(); 17 | app.set('port', config.PORT); 18 | 19 | app.use(cors()); 20 | app.use(bodyParser.json()); 21 | app.use(bodyParser.urlencoded({extended: true})); 22 | app.use(domainMiddleware); 23 | 24 | 25 | const apiRouter = new express.Router(); 26 | 27 | loadRoutes(apiRouter, routes); 28 | 29 | app.use('/api', apiRouter); 30 | 31 | app.use(errorHandler({ 32 | log: ({err, req, body}) => { 33 | logger.error(err, `${body.status} ${req.method} ${req.url}`); 34 | }, 35 | })); 36 | 37 | app.use(notFoundHandler({ 38 | log: ({req}) => { 39 | logger.error(`404 ${req.method} ${req.url}`); 40 | }, 41 | })); 42 | 43 | if (!module.parent) { 44 | app.listen(app.get('port'), () => { 45 | logger.info(`Express server listening on port ${app.get('port')} in ${process.env.NODE_ENV} mode`); 46 | }); 47 | } 48 | 49 | export default app; 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Blue mix starter 2 | 3 | ## Description 4 | A starter pack for nodejs with bluemix deployment. 5 | 6 | ## Requirements 7 | * node `^7.2.0` https://nodejs.org/en/ 8 | * npm `^3.10.0` 9 | 10 | ## Configuration 11 | 12 | |Name|Description| 13 | |----|-----------| 14 | |`PORT`| The port to listen| 15 | |`VERBOSE_LOGGING`| The flag if debug logging in enabled| 16 | 17 | 18 | ## Local deployment 19 | 20 | |`npm run