├── .npmrc ├── .gitattributes ├── .codeclimate.yml ├── .travis.yml ├── config ├── winston.js ├── env │ ├── test.js │ ├── index.js │ ├── development.js │ └── production.js ├── config.json ├── param-validation.js └── express.js ├── .editorconfig ├── server ├── models │ ├── schedule.js │ ├── client.js │ ├── user.js │ └── index.js ├── modules │ ├── Schedule │ │ ├── routes.js │ │ └── controller.js │ ├── Auth │ │ ├── routes.js │ │ └── controller.js │ ├── User │ │ ├── routes.js │ │ └── controller.js │ └── Client │ │ ├── routes.js │ │ └── controller.js ├── tests │ ├── misc.test.js │ └── user.test.js └── helpers │ └── APIError.js ├── .istanbul.yml ├── migrations ├── 20161215131754-create-user-migration.js ├── 20161215165843-create-schedule-migration.js └── 20161215165911-create-client-migration.js ├── .gitignore ├── .eslintrc ├── index.js ├── README.md ├── gulpfile.babel.js └── package.json /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Convert text file line endings to lf 2 | * text=auto 3 | *.js text 4 | # Denote all files that are truly binary and should not be modified. 5 | *.mp4 binary 6 | *.jpg binary 7 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | eslint: 3 | enabled: true 4 | ratings: 5 | paths: 6 | - server/** 7 | - config/** 8 | - "**.js" 9 | exclude_paths: 10 | - node_modules/**/* 11 | - server/tests/** 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.6" 4 | - "6.9" 5 | - "7.2" 6 | services: 7 | - mongodb 8 | cache: 9 | directories: 10 | - node_modules 11 | git: 12 | depth: 3 13 | after_script: 14 | - yarn report-coverage -------------------------------------------------------------------------------- /config/winston.js: -------------------------------------------------------------------------------- 1 | import winston from 'winston'; 2 | 3 | const logger = new (winston.Logger)({ 4 | transports: [ 5 | new (winston.transports.Console)({ 6 | json: true, 7 | colorize: true 8 | }) 9 | ] 10 | }); 11 | 12 | export default logger; 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /config/env/test.js: -------------------------------------------------------------------------------- 1 | export default { 2 | env: 'test', 3 | jwtSecret: '0a6b944d-d2fb-46fc-a85e-0295c986cd9f', 4 | port: 4040, 5 | db: { 6 | username: 'postgres', 7 | password: '250494', 8 | database: 'artwork', 9 | host: '127.0.0.1', 10 | port: '5432', 11 | dialect: 'postgres' 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /config/env/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | const env = process.env.NODE_ENV || 'development'; 4 | const config = require(`./${env}`); // eslint-disable-line import/no-dynamic-require 5 | 6 | const defaults = { 7 | root: path.join(__dirname, '/..') 8 | }; 9 | 10 | export default Object.assign(defaults, config); 11 | -------------------------------------------------------------------------------- /config/env/development.js: -------------------------------------------------------------------------------- 1 | export default { 2 | env: 'development', 3 | jwtSecret: '0a6b944d-d2fb-46fc-a85e-0295c986cd9f', 4 | port: 4040, 5 | db: { 6 | username: 'postgres', 7 | password: '250494', 8 | database: 'artwork', 9 | host: '127.0.0.1', 10 | port: '5432', 11 | dialect: 'postgres' 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /config/env/production.js: -------------------------------------------------------------------------------- 1 | export default { 2 | env: 'production', 3 | jwtSecret: '0a6b944d-d2fb-46fc-a85e-0295c986cd9f', 4 | port: 4040, 5 | db: { 6 | username: 'postgres', 7 | password: '250494', 8 | database: 'artwork', 9 | host: '127.0.0.1', 10 | port: '5432', 11 | dialect: 'postgres' 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /server/models/schedule.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function scheduleSchema(sequelize, DataTypes) { 4 | const Schedule = sequelize.define('Schedule', { 5 | title: DataTypes.STRING, 6 | clientId: DataTypes.INTEGER, 7 | start: DataTypes.DATE, 8 | end: DataTypes.DATE, 9 | price: DataTypes.FLOAT 10 | }); 11 | return Schedule; 12 | } 13 | 14 | export default scheduleSchema; -------------------------------------------------------------------------------- /server/modules/Schedule/routes.js: -------------------------------------------------------------------------------- 1 | const ScheduleCtrl = require('./controller'); 2 | const router = require('express').Router(); 3 | const expressJwt = require('express-jwt'); 4 | const config = require('../../../config/env'); 5 | const jwtAuth = expressJwt({ secret: config.jwtSecret }); 6 | 7 | router.route('/') 8 | .get(jwtAuth, ScheduleCtrl.getComments); 9 | 10 | 11 | export default router; -------------------------------------------------------------------------------- /server/modules/Schedule/controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const models = require('../../models/index'); 4 | 5 | /** 6 | * Load comments and append to req. 7 | */ 8 | async function getComments(req, res, next) { 9 | try { 10 | const data = await models.Schedule.findAll(); 11 | res.json({success: true, data}); 12 | } catch (err) { 13 | next(err); 14 | } 15 | } 16 | 17 | export { getComments }; 18 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | verbose: false 2 | instrumentation: 3 | excludes: ['dist/**', 'coverage/**', 'gulpfile.babel.js'] 4 | include-all-sources: true 5 | reporting: 6 | print: summary 7 | reports: 8 | - lcov 9 | dir: ./coverage 10 | watermarks: 11 | statements: [50, 80] 12 | lines: [50, 80] 13 | functions: [50, 80] 14 | branches: [50, 80] 15 | check: 16 | global: 17 | statements: 50 18 | lines: 50 19 | branches: 50 20 | functions: 50 21 | each: 22 | statements: 50 23 | lines: 50 24 | branches: 50 25 | functions: 20 -------------------------------------------------------------------------------- /server/modules/Auth/routes.js: -------------------------------------------------------------------------------- 1 | const AuthCtrl = require('./controller'); 2 | const router = require('express').Router(); 3 | const expressJwt = require('express-jwt'); 4 | const config = require('../../../config/env'); 5 | 6 | 7 | /** POST /api/auth/login - Returns token if correct username and password is provided */ 8 | router.route('/login').post(AuthCtrl.login); 9 | 10 | /** GET /api/auth/random-number - Protected route, 11 | * needs token returned by the above as header. Authorization: Bearer {token} */ 12 | router.route('/random-number').get(expressJwt({ secret: config.jwtSecret }), AuthCtrl.getRandomNumber); 13 | 14 | export default router; -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "username": "postgres", 4 | "password": "250494", 5 | "database": "artwork", 6 | "host": "127.0.0.1", 7 | "dialect": "postgres", 8 | "port": "5432" 9 | }, 10 | "test": { 11 | "username": "postgres", 12 | "password": "250494", 13 | "database": "artwork", 14 | "host": "127.0.0.1", 15 | "dialect": "postgres", 16 | "port": "5432" 17 | }, 18 | "production": { 19 | "username": "postgres", 20 | "password": "250494", 21 | "database": "artwork", 22 | "host": "127.0.0.1", 23 | "dialect": "postgres", 24 | "port": "5432" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/tests/misc.test.js: -------------------------------------------------------------------------------- 1 | import request from 'supertest-as-promised'; 2 | import httpStatus from 'http-status'; 3 | import chai, { expect } from 'chai'; 4 | import app from '../../index'; 5 | 6 | chai.config.includeStack = true; 7 | 8 | describe('## Misc', () => { 9 | 10 | describe('# GET /apiv1/404', () => { 11 | it('should return 404 status', (done) => { 12 | request(app) 13 | .get('/apiv1/404') 14 | .expect(httpStatus.NOT_FOUND) 15 | .then((res) => { 16 | expect(res.body.message).to.equal('Not Found'); 17 | done(); 18 | }) 19 | .catch(done); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /server/models/client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function clientSchema(sequelize, DataTypes) { 4 | var Client = sequelize.define('Client', { 5 | name: DataTypes.STRING, 6 | lastName: DataTypes.STRING, 7 | rg: DataTypes.STRING, 8 | cpf: DataTypes.STRING, 9 | maritalStatus: DataTypes.STRING, 10 | sex: DataTypes.STRING, 11 | city: DataTypes.STRING, 12 | address: DataTypes.STRING, 13 | state: DataTypes.STRING, 14 | phone: DataTypes.STRING, 15 | facebook: DataTypes.STRING, 16 | email: DataTypes.STRING, 17 | birthday: DataTypes.STRING, 18 | info: DataTypes.STRING 19 | }); 20 | 21 | return Client; 22 | } 23 | 24 | export default clientSchema; -------------------------------------------------------------------------------- /server/models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bcrypt = require('bcrypt'); 3 | 4 | function userSchema(sequelize, DataTypes) { 5 | var User = sequelize.define('User', { 6 | username: DataTypes.STRING, 7 | password: DataTypes.STRING 8 | }); 9 | 10 | User.beforeCreate(function(model, options, cb) { 11 | bcrypt.genSalt(10, function(err, salt) { 12 | if (err) { 13 | return next(err); 14 | } 15 | bcrypt.hash(model.password, salt, function(err, hash) { 16 | if (err) { 17 | return next(err); 18 | } 19 | model.password = hash; 20 | return cb(null, options); 21 | }); 22 | }); 23 | 24 | }); 25 | 26 | 27 | return User; 28 | } 29 | 30 | export default userSchema; 31 | -------------------------------------------------------------------------------- /migrations/20161215131754-create-user-migration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: function(queryInterface, Sequelize) { 4 | return queryInterface.createTable('Users', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | username: { 12 | type: Sequelize.STRING 13 | }, 14 | password: { 15 | type: Sequelize.STRING 16 | }, 17 | createdAt: { 18 | allowNull: false, 19 | type: Sequelize.DATE 20 | }, 21 | updatedAt: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | } 25 | }); 26 | }, 27 | down: function(queryInterface, Sequelize) { 28 | return queryInterface.dropTable('Users'); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # IDE 12 | .idea 13 | 14 | # OS generated files 15 | .DS_Store 16 | .DS_Store? 17 | ._* 18 | .Spotlight-V100 19 | ehthumbs.db 20 | Icon? 21 | Thumbs.db 22 | 23 | # Babel ES6 compiles files 24 | dist 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (http://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directory 42 | node_modules 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional REPL history 48 | .node_repl_history 49 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "indent": [ 4 | 2, 5 | 2, 6 | { 7 | "SwitchCase": 1 8 | } 9 | ], 10 | "space-before-function-paren": [ 11 | 2, 12 | { 13 | "anonymous": "always", 14 | "named": "never" 15 | } 16 | ], 17 | "no-use-before-define": [ 18 | 2, 19 | "nofunc" 20 | ], 21 | // TODO: turn on later 22 | "comma-dangle": [ 23 | 0 24 | ], 25 | "import/no-extraneous-dependencies": [ 26 | "error", 27 | { 28 | "devDependencies": true 29 | } 30 | ], 31 | "no-underscore-dangle": [ 32 | 0 33 | ] 34 | }, 35 | "env": { 36 | "node": true, 37 | "mocha": true 38 | }, 39 | "parserOptions": { 40 | "ecmaVersion": 6, 41 | "sourceType": "module" 42 | }, 43 | "extends": [ 44 | "eslint:recommended", 45 | "airbnb-base" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /server/models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import Sequelize from 'sequelize'; 6 | import config from '../../config/env'; 7 | const basename = path.basename(module.filename); 8 | const db = {}; 9 | 10 | const sequelize = new Sequelize(config.db.database, config.db.username, config.db.password, config.db); 11 | 12 | fs 13 | .readdirSync(__dirname) 14 | .filter(function(file) { 15 | return (file.indexOf('.') !== 0) && (file !== basename); 16 | }) 17 | .forEach(function(file) { 18 | if (file.slice(-3) !== '.js') return; 19 | let model = sequelize['import'](path.join(__dirname, file)); 20 | db[model.name] = model; 21 | }); 22 | 23 | Object.keys(db).forEach(function(modelName) { 24 | if (db[modelName].associate) { 25 | db[modelName].associate(db); 26 | } 27 | }); 28 | 29 | db.sequelize = sequelize; 30 | db.Sequelize = Sequelize; 31 | 32 | export default db; 33 | -------------------------------------------------------------------------------- /server/helpers/APIError.js: -------------------------------------------------------------------------------- 1 | import httpStatus from 'http-status'; 2 | 3 | /** 4 | * @extends Error 5 | */ 6 | class ExtendableError extends Error { 7 | constructor(message, status, isPublic) { 8 | super(message); 9 | this.name = this.constructor.name; 10 | this.message = message; 11 | this.status = status; 12 | this.isPublic = isPublic; 13 | this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore. 14 | Error.captureStackTrace(this, this.constructor.name); 15 | } 16 | } 17 | 18 | /** 19 | * Class representing an API error. 20 | * @extends ExtendableError 21 | */ 22 | class APIError extends ExtendableError { 23 | /** 24 | * Creates an API error. 25 | * @param {string} message - Error message. 26 | * @param {number} status - HTTP status code of error. 27 | * @param {boolean} isPublic - Whether the message should be visible to user or not. 28 | */ 29 | constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false) { 30 | super(message, status, isPublic); 31 | } 32 | } 33 | 34 | export default APIError; 35 | -------------------------------------------------------------------------------- /migrations/20161215165843-create-schedule-migration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: function(queryInterface, Sequelize) { 4 | return queryInterface.createTable('Schedules', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | title: { 12 | type: Sequelize.STRING 13 | }, 14 | clientId: { 15 | type: Sequelize.INTEGER 16 | }, 17 | start: { 18 | allowNull: false, 19 | type: Sequelize.DATE 20 | }, 21 | end: { 22 | allowNull: false, 23 | type: Sequelize.DATE 24 | }, 25 | price: { 26 | allowNull: false, 27 | type: Sequelize.FLOAT 28 | }, 29 | createdAt: { 30 | allowNull: false, 31 | type: Sequelize.DATE 32 | }, 33 | updatedAt: { 34 | allowNull: false, 35 | type: Sequelize.DATE 36 | } 37 | }); 38 | }, 39 | down: function(queryInterface, Sequelize) { 40 | return queryInterface.dropTable('Schedules'); 41 | } 42 | }; -------------------------------------------------------------------------------- /server/modules/User/routes.js: -------------------------------------------------------------------------------- 1 | const UserCtrl = require('./controller'); 2 | const router = require('express').Router(); 3 | const validate = require ('express-validation'); 4 | const paramValidation = require('../../../config/param-validation'); 5 | const expressJwt = require('express-jwt'); 6 | const config = require('../../../config/env'); 7 | const jwtAuth = expressJwt({ secret: config.jwtSecret }); 8 | 9 | router.route('/') 10 | /** GET /api/users - Get list of users */ 11 | .get(jwtAuth, UserCtrl.list) 12 | 13 | /** POST /api/users - Create new user */ 14 | //validate(paramValidation.createUser), 15 | .post(UserCtrl.create); 16 | 17 | router.route('/:userId') 18 | /** GET /api/users/:userId - Get user */ 19 | .get(jwtAuth, UserCtrl.get) 20 | 21 | /** PUT /api/users/:userId - Update user */ 22 | //validate(paramValidation.updateUser) 23 | .put(jwtAuth, UserCtrl.update) 24 | 25 | /** DELETE /api/users/:userId - Delete user */ 26 | .delete(jwtAuth, UserCtrl.remove); 27 | 28 | /** Load user when API with userId route parameter is hit */ 29 | //router.param('userId', UserCtrl.load); 30 | 31 | export default router; 32 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import util from 'util'; 3 | import config from './config/env'; 4 | import app from './config/express'; 5 | 6 | const debug = require('debug')('express-sequelize-es7-rest-api:index'); 7 | 8 | // make bluebird default Promise 9 | Promise = require('bluebird'); // eslint-disable-line no-global-assign 10 | 11 | /* plugin bluebird promise in mongoose 12 | mongoose.Promise = Promise; 13 | 14 | // connect to mongo db 15 | mongoose.connect(config.db, { server: { socketOptions: { keepAlive: 1 } } }); 16 | mongoose.connection.on('error', () => { 17 | throw new Error(`unable to connect to database: ${config.db}`); 18 | }); 19 | 20 | // print mongoose logs in dev env 21 | if (config.MONGOOSE_DEBUG) { 22 | mongoose.set('debug', (collectionName, method, query, doc) => { 23 | debug(`${collectionName}.${method}`, util.inspect(query, false, 20), doc); 24 | }); 25 | }*/ 26 | 27 | // module.parent check is required to support mocha watch 28 | // src: https://github.com/mochajs/mocha/issues/1912 29 | if (!module.parent) { 30 | // listen on port config.port 31 | app.listen(config.port, () => { 32 | debug(`server started on port ${config.port} (${config.env})`); 33 | }); 34 | } 35 | 36 | export default app; 37 | -------------------------------------------------------------------------------- /server/modules/Client/routes.js: -------------------------------------------------------------------------------- 1 | const ClientCtrl = require('./controller'); 2 | const router = require('express').Router(); 3 | const validate = require ('express-validation'); 4 | const paramValidation = require('../../../config/param-validation'); 5 | const expressJwt = require('express-jwt'); 6 | const config = require('../../../config/env'); 7 | const jwtAuth = expressJwt({ secret: config.jwtSecret }); 8 | 9 | router.route('/') 10 | /** GET /api/clients - Get list of clients */ 11 | .get(jwtAuth, ClientCtrl.list) 12 | 13 | /** POST /api/clients - Create new client */ 14 | .post(jwtAuth, validate(paramValidation.createClient), ClientCtrl.create); 15 | 16 | router.route('/:clientId') 17 | /** GET /api/clients/:clientId - Get client */ 18 | .get(jwtAuth, ClientCtrl.get) 19 | 20 | /** PUT /api/clients/:clientId - Update client */ 21 | .put(jwtAuth, validate(paramValidation.updateClient), ClientCtrl.update) 22 | 23 | /** DELETE /api/clients/:clientId - Delete client */ 24 | .delete(jwtAuth, ClientCtrl.remove); 25 | 26 | 27 | router.route('/:clientId/schedules') 28 | .post(jwtAuth, validate(paramValidation.createSchedule), ClientCtrl.createSchedule) 29 | 30 | .get(jwtAuth, ClientCtrl.getSchedulesByClientId); 31 | 32 | router.route('/:clientId/schedules/:scheduleId') 33 | .delete(jwtAuth, ClientCtrl.removeSchedule); 34 | 35 | 36 | /** Load client when API with clientId route parameter is hit */ 37 | //router.param('clientId', ClientCtrl.load); 38 | 39 | export default router; -------------------------------------------------------------------------------- /server/modules/Auth/controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const jwt = require('jsonwebtoken'); 3 | const config = require('../../../config/env'); 4 | const models = require('../../models/index'); 5 | const bcrypt = require('bcrypt'); 6 | 7 | /** 8 | * Returns jwt token if valid username and password is provided 9 | * @param req 10 | * @param res 11 | * @param next 12 | * @returns {*} 13 | */ 14 | async function login(req, res, next) { 15 | try { 16 | 17 | const data = await models.User 18 | .find({ where:{ username: req.body.username }}); 19 | 20 | if(data != null && comparePassword(req.body.password, data.password) ) { 21 | const token = jwt.sign({ username: data.username }, config.jwtSecret); 22 | return res.json({ token, username: data.username }); 23 | } 24 | 25 | return res.json({ success: false }); 26 | 27 | } catch (err) { 28 | next(err); 29 | } 30 | } 31 | 32 | function comparePassword(reqPassword, password) { 33 | return bcrypt.compareSync(reqPassword, password); 34 | } 35 | 36 | /** 37 | * This is a protected route. Will return random number only if jwt token is provided in header. 38 | * @param req 39 | * @param res 40 | * @returns {*} 41 | */ 42 | function getRandomNumber(req, res) { 43 | // req.user is assigned by jwt middleware if valid token is provided 44 | return res.json({ 45 | user: req.user, 46 | num: Math.random() * 100 47 | }); 48 | } 49 | 50 | 51 | export { login, getRandomNumber }; 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Express & sequelize REST API Boilerplate in ES7 2 | 3 | ## Getting Started 4 | Clone the repo: 5 | ```sh 6 | git clone git@github.com:pedrobarrostech/express-sequelize-es7-rest-api.git 7 | cd express-sequelize-es7-rest-api 8 | ``` 9 | 10 | Install yarn: 11 | ```js 12 | npm install -g yarn 13 | ``` 14 | 15 | Install dependencies: 16 | ```sh 17 | yarn 18 | ``` 19 | 20 | Start server: 21 | ```sh 22 | # Start server 23 | yarn start 24 | 25 | # Selectively set DEBUG env var to get logs 26 | DEBUG=express-sequelize-es7-rest-api:* yarn start 27 | ``` 28 | Refer [debug](https://www.npmjs.com/package/debug) to know how to selectively turn on logs. 29 | 30 | 31 | Tests: 32 | ```sh 33 | # Run tests written in ES6 along with code coverage 34 | yarn test 35 | 36 | # Run tests on file change 37 | yarn test:watch 38 | 39 | # Run tests enforcing code coverage (configured via .istanbul.yml) 40 | yarn test:check-coverage 41 | ``` 42 | 43 | Lint: 44 | ```sh 45 | # Lint code with ESLint 46 | yarn lint 47 | 48 | # Run lint on any file change 49 | yarn lint:watch 50 | ``` 51 | 52 | Other gulp tasks: 53 | ```sh 54 | # Wipe out dist and coverage directory 55 | gulp clean 56 | 57 | # Default task: Wipes out dist and coverage directory. Compiles using babel. 58 | gulp 59 | ``` 60 | 61 | ##### Deployment 62 | 63 | ```sh 64 | # compile to ES5 65 | 1. yarn build 66 | 67 | # upload dist/ to your server 68 | 2. scp -rp dist/ user@dest:/path 69 | 70 | # install production dependencies only 71 | 3. yarn --production 72 | 73 | # Use any process manager to start your services 74 | 4. pm2 start dist/index.js 75 | ``` 76 | -------------------------------------------------------------------------------- /migrations/20161215165911-create-client-migration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: function(queryInterface, Sequelize) { 4 | return queryInterface.createTable('Clients', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | name: { 12 | type: Sequelize.STRING 13 | }, 14 | lastName: { 15 | type: Sequelize.STRING 16 | }, 17 | rg: { 18 | type: Sequelize.STRING 19 | }, 20 | cpf: { 21 | type: Sequelize.STRING 22 | }, 23 | maritalStatus: { 24 | type: Sequelize.STRING 25 | }, 26 | sex: { 27 | type: Sequelize.STRING 28 | }, 29 | city: { 30 | type: Sequelize.STRING 31 | }, 32 | address: { 33 | type: Sequelize.STRING 34 | }, 35 | state: { 36 | type: Sequelize.STRING 37 | }, 38 | phone: { 39 | type: Sequelize.STRING 40 | }, 41 | facebook: { 42 | type: Sequelize.STRING 43 | }, 44 | email: { 45 | type: Sequelize.STRING 46 | }, 47 | birthday: { 48 | type: Sequelize.STRING 49 | }, 50 | info: { 51 | type: Sequelize.STRING 52 | }, 53 | createdAt: { 54 | allowNull: false, 55 | type: Sequelize.DATE 56 | }, 57 | updatedAt: { 58 | allowNull: false, 59 | type: Sequelize.DATE 60 | } 61 | }); 62 | }, 63 | down: function(queryInterface, Sequelize) { 64 | return queryInterface.dropTable('Clients'); 65 | } 66 | }; -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import gulpLoadPlugins from 'gulp-load-plugins'; 3 | import path from 'path'; 4 | import del from 'del'; 5 | import runSequence from 'run-sequence'; 6 | 7 | const plugins = gulpLoadPlugins(); 8 | 9 | const paths = { 10 | js: ['./**/*.js', '!dist/**', '!node_modules/**', '!coverage/**'], 11 | nonJs: ['./package.json', './.gitignore'], 12 | tests: './server/tests/*.js' 13 | }; 14 | 15 | // Clean up dist and coverage directory 16 | gulp.task('clean', () => 17 | del(['dist/**', 'coverage/**', '!dist', '!coverage']) 18 | ); 19 | 20 | // Copy non-js files to dist 21 | gulp.task('copy', () => 22 | gulp.src(paths.nonJs) 23 | .pipe(plugins.newer('dist')) 24 | .pipe(gulp.dest('dist')) 25 | ); 26 | 27 | // Compile ES6 to ES5 and copy to dist 28 | gulp.task('babel', () => 29 | gulp.src([...paths.js, '!gulpfile.babel.js'], { base: '.' }) 30 | .pipe(plugins.newer('dist')) 31 | .pipe(plugins.sourcemaps.init()) 32 | .pipe(plugins.babel()) 33 | .pipe(plugins.sourcemaps.write('.', { 34 | includeContent: false, 35 | sourceRoot(file) { 36 | return path.relative(file.path, __dirname); 37 | } 38 | })) 39 | .pipe(gulp.dest('dist')) 40 | ); 41 | 42 | // Start server with restart on file changes 43 | gulp.task('nodemon', ['copy', 'babel'], () => 44 | plugins.nodemon({ 45 | script: path.join('dist', 'index.js'), 46 | ext: 'js', 47 | ignore: ['node_modules/**/*.js', 'dist/**/*.js'], 48 | tasks: ['copy', 'babel'] 49 | }) 50 | ); 51 | 52 | // gulp serve for development 53 | gulp.task('serve', ['clean'], () => runSequence('nodemon')); 54 | 55 | // default task: clean dist, compile js files and copy non-js files. 56 | gulp.task('default', ['clean'], () => { 57 | runSequence( 58 | ['copy', 'babel'] 59 | ); 60 | }); 61 | -------------------------------------------------------------------------------- /config/param-validation.js: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | const validations = { 3 | // POST /api/users 4 | createUser: { 5 | body: { 6 | username: Joi.string().required(), 7 | password: Joi.string().required() 8 | } 9 | }, 10 | 11 | createSchedule: { 12 | body: { 13 | title: Joi.string().required() 14 | } 15 | }, 16 | 17 | // UPDATE /api/users/:userId 18 | updateUser: { 19 | body: { 20 | username: Joi.string().required(), 21 | password: Joi.string().required() 22 | }, 23 | params: { 24 | userId: Joi.string().hex().required() 25 | } 26 | }, 27 | 28 | // POST /api/clients 29 | createClient: { 30 | body: { 31 | name: Joi.string().required(), 32 | lastName: Joi.string().required(), 33 | rg: Joi.string().required(), 34 | cpf: Joi.string().required(), 35 | maritalStatus: Joi.string().required(), 36 | sex: Joi.string().required(), 37 | address: Joi.string().required(), 38 | city: Joi.string().required(), 39 | state: Joi.string().required(), 40 | phone: Joi.string().required(), 41 | facebook: Joi.string().required(), 42 | email: Joi.string().required(), 43 | birthday: Joi.string().required(), 44 | info: Joi.string().required() 45 | } 46 | }, 47 | 48 | // UPDATE /api/clients/:clientId 49 | updateClient: { 50 | body: { 51 | name: Joi.string().required(), 52 | lastName: Joi.string().required(), 53 | rg: Joi.string().required(), 54 | cpf: Joi.string().required(), 55 | maritalStatus: Joi.string().required(), 56 | sex: Joi.string().required(), 57 | address: Joi.string().required(), 58 | city: Joi.string().required(), 59 | state: Joi.string().required(), 60 | phone: Joi.string().required(), 61 | facebook: Joi.string().required(), 62 | email: Joi.string().required(), 63 | birthday: Joi.string().required(), 64 | info: Joi.string().required() 65 | }, 66 | params: { 67 | clientId: Joi.string().hex().required() 68 | } 69 | }, 70 | 71 | // POST /api/auth/login 72 | login: { 73 | body: { 74 | username: Joi.string().required(), 75 | password: Joi.string().required() 76 | } 77 | } 78 | }; 79 | 80 | export default validations; -------------------------------------------------------------------------------- /server/tests/user.test.js: -------------------------------------------------------------------------------- 1 | import request from 'supertest-as-promised'; 2 | import httpStatus from 'http-status'; 3 | import chai, { expect } from 'chai'; 4 | import app from '../../index'; 5 | 6 | chai.config.includeStack = true; 7 | 8 | describe('## User APIs', () => { 9 | let user = { 10 | id: '', 11 | username: 'fulano', 12 | password: '1234567890' 13 | }; 14 | 15 | describe('# POST /apiv1/users', () => { 16 | it('should create a new user', async () => { 17 | 18 | const res = await request(app) 19 | .post(`/apiv1/users`) 20 | .set('content-type', 'application/json') 21 | .send(user); 22 | 23 | user.id = res.body.data.id; 24 | expect(res.body.data.username).to.equal(user.username); 25 | expect(httpStatus.OK); 26 | }); 27 | }); 28 | 29 | 30 | describe('# GET /apiv1/users/:userId', () => { 31 | it('should get user details', async () => { 32 | const res = await request(app) 33 | .get(`/apiv1/users/${user.id}`) 34 | .set('content-type', 'application/json') 35 | 36 | expect(res.body.data.username).to.equal(user.username); 37 | expect(httpStatus.OK); 38 | }); 39 | 40 | it('should report error with message - Not found, when user does not exists', async () => { 41 | const res = await request(app) 42 | .get('/apiv1/users/0') 43 | .set('content-type', 'application/json') 44 | 45 | expect(res.body.data).to.equal(null); 46 | }); 47 | 48 | }); 49 | 50 | 51 | describe('# PUT /apiv1/users/:userId', () => { 52 | it('should update user details', async () => { 53 | let userEdit = {}; 54 | userEdit.username = 'kk'; 55 | userEdit.password = 'kk'; 56 | 57 | const res = await request(app) 58 | .put(`/apiv1/users/${user.id}`) 59 | .set('content-type', 'application/json') 60 | .send(userEdit); 61 | 62 | expect(res.body.data.username).to.equal('kk'); 63 | expect(httpStatus.OK); 64 | 65 | }); 66 | }); 67 | 68 | 69 | describe('# GET /apiv1/users/', () => { 70 | it('should get all users', async () => { 71 | const res = await request(app) 72 | .get(`/apiv1/users`) 73 | .set('content-type', 'application/json'); 74 | 75 | expect(res.body.data).to.be.an('array'); 76 | 77 | }); 78 | }); 79 | 80 | 81 | describe('# DELETE /apiv1/users/', () => { 82 | it('should delete user', async () => { 83 | 84 | const res = await request(app) 85 | .delete(`/apiv1/users/${user.id}`) 86 | .set('content-type', 'application/json'); 87 | 88 | expect(res.body.success).to.equal(true); 89 | expect(httpStatus.OK); 90 | }); 91 | }); 92 | 93 | 94 | }); 95 | -------------------------------------------------------------------------------- /server/modules/User/controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import models from '../../models/index'; 4 | 5 | /** 6 | * GET / 7 | * @param req 8 | * @param res 9 | * @param next 10 | */ 11 | async function list(req, res, next) { 12 | try { 13 | const data = await models.User.findAll({}); 14 | res.json({success: true, data}); 15 | } catch (err) { 16 | next(err); 17 | } 18 | } 19 | 20 | 21 | /** 22 | * Get User 23 | * @returns {User} 24 | */ 25 | async function get(req, res, next) { 26 | try { 27 | const data = await models.User 28 | .find({ where: { id: req.params.userId } }); 29 | res.json({ data }); 30 | } catch (err) { 31 | next(err); 32 | } 33 | } 34 | 35 | 36 | /** 37 | * Get User 38 | * @returns {User} 39 | */ 40 | async function load(req, res, next, id) { 41 | try { 42 | const data = await models.User 43 | .find({ where: { id: req.params.userId } }); 44 | res.json({success: true, data}); 45 | } catch (err) { 46 | next(err); 47 | } 48 | } 49 | 50 | 51 | /** 52 | * Create new User 53 | * @property {string} req.body.username - The username of User. 54 | * @property {string} req.body.password - The password of User. 55 | * @returns {User} 56 | */ 57 | async function create(req, res, next) { 58 | try { 59 | const user = { 60 | username: req.body.username, 61 | password: req.body.password 62 | }; 63 | const data = await models.User.create(user); 64 | res.json({success: true, data}); 65 | } catch (err) { 66 | next(err); 67 | } 68 | } 69 | 70 | 71 | /** 72 | * Update existing User 73 | * @property {string} req.body.username - The username of User. 74 | * @property {string} req.body.password - The password of User. 75 | * @returns {User} 76 | */ 77 | async function update(req, res, next) { 78 | try { 79 | const user = await models.User 80 | .find({ where: { id: req.params.userId } }); 81 | if(user){ 82 | const data = await user 83 | .updateAttributes({ 84 | username: req.body.username, 85 | password: req.body.password 86 | }); 87 | res.json({success: true, data}); 88 | } 89 | } catch (err) { 90 | next(err); 91 | } 92 | } 93 | 94 | 95 | /** 96 | * Deconste User. 97 | * @returns {User} 98 | */ 99 | async function remove(req, res, next) { 100 | try { 101 | const data = await models.User 102 | .destroy({ where: { id: req.params.userId } }); 103 | res.json({success: true, data}); 104 | } catch (err) { 105 | next(err); 106 | } 107 | } 108 | 109 | export { load, list, get, create, update, remove }; 110 | -------------------------------------------------------------------------------- /config/express.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import logger from 'morgan'; 3 | import bodyParser from 'body-parser'; 4 | import cookieParser from 'cookie-parser'; 5 | import compress from 'compression'; 6 | import methodOverride from 'method-override'; 7 | import cors from 'cors'; 8 | import httpStatus from 'http-status'; 9 | import expressWinston from 'express-winston'; 10 | import expressValidation from 'express-validation'; 11 | import helmet from 'helmet'; 12 | import winstonInstance from './winston'; 13 | import config from './env'; 14 | import APIError from '../server/helpers/APIError'; 15 | 16 | const clientRoutes = require('../server/modules/Client/routes'); 17 | const userRoutes = require('../server/modules/User/routes'); 18 | const authRoutes = require('../server/modules/Auth/routes'); 19 | const scheduleRoutes = require('../server/modules/Schedule/routes'); 20 | const app = express(); 21 | 22 | if (config.env === 'development') { 23 | app.use(logger('dev')); 24 | } 25 | 26 | // parse body params and attache them to req.body 27 | app.use(bodyParser.json()); 28 | app.use(bodyParser.urlencoded({ extended: true })); 29 | 30 | app.use(cookieParser()); 31 | app.use(compress()); 32 | app.use(methodOverride()); 33 | 34 | // secure apps by setting various HTTP headers 35 | app.use(helmet()); 36 | 37 | // enable CORS - Cross Origin Resource Sharing 38 | app.use(cors()); 39 | 40 | // enable detailed API logging in dev env 41 | if (config.env === 'development') { 42 | expressWinston.requestWhitelist.push('body'); 43 | expressWinston.responseWhitelist.push('body'); 44 | app.use(expressWinston.logger({ 45 | winstonInstance, 46 | meta: true, // optional: log meta data about request (defaults to true) 47 | msg: 'HTTP {{req.method}} {{req.url}} {{res.statusCode}} {{res.responseTime}}ms', 48 | colorStatus: true // Color the status code (default green, 3XX cyan, 4XX yellow, 5XX red). 49 | })); 50 | } 51 | 52 | // mount all routes on /api path 53 | //app.use('/api', routes); 54 | 55 | // 56 | // serve API V1 routes 57 | /////////////////////////////////////////////////////////// 58 | app.use('/apiv1/clients', clientRoutes); 59 | app.use('/apiv1/users', userRoutes); 60 | app.use('/apiv1/schedules', scheduleRoutes); 61 | app.use('/apiv1/auth', authRoutes); 62 | 63 | // if error is not an instanceOf APIError, convert it. 64 | app.use((err, req, res, next) => { 65 | if (err instanceof expressValidation.ValidationError) { 66 | // validation error contains errors which is an array of error each containing message[] 67 | const unifiedErrorMessage = err.errors.map(error => error.messages.join('. ')).join(' and '); 68 | const error = new APIError(unifiedErrorMessage, err.status, true); 69 | return next(error); 70 | } else if (!(err instanceof APIError)) { 71 | const apiError = new APIError(err.message, err.status, err.isPublic); 72 | return next(apiError); 73 | } 74 | return next(err); 75 | }); 76 | 77 | // catch 404 and forward to error handler 78 | app.use((req, res, next) => { 79 | const err = new APIError('API not found', httpStatus.NOT_FOUND); 80 | return next(err); 81 | }); 82 | 83 | // log error in winston transports except when executing test suite 84 | if (config.env !== 'test') { 85 | app.use(expressWinston.errorLogger({ 86 | winstonInstance 87 | })); 88 | } 89 | 90 | // error handler, send stacktrace only during development 91 | app.use((err, req, res, next) => // eslint-disable-line no-unused-vars 92 | res.status(err.status).json({ 93 | message: err.isPublic ? err.message : httpStatus[err.status], 94 | stack: config.env === 'development' ? err.stack : {} 95 | }) 96 | ); 97 | 98 | 99 | export default app; 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-sequelize-es7-rest-api", 3 | "version": "1.0.0", 4 | "description": "A Boilerplate application for building REST APIs using express, sequelize in ES7 with code coverage", 5 | "author": "Pedro Barros ", 6 | "main": "index.js", 7 | "private": false, 8 | "engines": { 9 | "node": ">=4.6.0", 10 | "npm": ">=2.15.9", 11 | "yarn": ">=0.17.9" 12 | }, 13 | "scripts": { 14 | "start": "gulp serve", 15 | "build": "gulp", 16 | "lint": "esw *.js server config --color", 17 | "lint:watch": "yarn lint -- --watch", 18 | "test": "cross-env NODE_ENV=test babel-node node_modules/isparta/bin/isparta cover ./node_modules/mocha/bin/_mocha -- --ui bdd --reporter spec --colors --compilers js:babel-core/register ./server/tests/*.test.js", 19 | "test:watch": "yarn test -- --watch", 20 | "test:check-coverage": "yarn test && istanbul check-coverage", 21 | "report-coverage": "coveralls < ./coverage/lcov.info" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git@github.com:pedrobarrostech/express-sequelize-es7-rest-api.git" 26 | }, 27 | "keywords": [ 28 | "express", 29 | "node", 30 | "node.js", 31 | "postgresql", 32 | "sequelize", 33 | "es7", 34 | "mocha", 35 | "istanbul", 36 | "REST", 37 | "API", 38 | "boilerplate" 39 | ], 40 | "dependencies": { 41 | "babel-runtime": "6.18.0", 42 | "bcrypt": "^0.8.7", 43 | "bluebird": "3.4.6", 44 | "body-parser": "1.15.2", 45 | "compression": "1.6.2", 46 | "cookie-parser": "1.4.3", 47 | "cors": "2.8.1", 48 | "cross-env": "3.1.3", 49 | "debug": "^2.2.0", 50 | "express": "4.14.0", 51 | "express-jwt": "5.1.0", 52 | "express-validation": "1.0.1", 53 | "express-winston": "2.0.0", 54 | "helmet": "3.1.0", 55 | "http-status": "^0.2.0", 56 | "joi": "9.2.0", 57 | "jsonwebtoken": "7.1.9", 58 | "method-override": "^2.3.5", 59 | "mongoose": "4.7.1", 60 | "morgan": "1.7.0", 61 | "winston": "2.3.0", 62 | "pg": "^4.4.3", 63 | "pg-hstore": "^2.3.2", 64 | "sequelize": "^3.12.2", 65 | "sequelize-cli": "^2.1.0" 66 | }, 67 | "devDependencies": { 68 | "babel-cli": "6.18.0", 69 | "babel-core": "6.18.0", 70 | "babel-plugin-add-module-exports": "0.2.1", 71 | "babel-plugin-transform-runtime": "6.15.0", 72 | "babel-preset-es2015": "6.16.0", 73 | "babel-preset-stage-3": "6.17.0", 74 | "chai": "^3.4.1", 75 | "commitizen": "^2.8.2", 76 | "coveralls": "^2.11.6", 77 | "cross-env": "3.1.3", 78 | "cz-conventional-changelog": "1.2.0", 79 | "del": "^2.2.0", 80 | "eslint": "3.11.1", 81 | "eslint-config-airbnb-base": "7.1.0", 82 | "eslint-plugin-import": "1.16.0", 83 | "eslint-watch": "2.1.14", 84 | "ghooks": "^1.2.4", 85 | "gulp": "3.9.1", 86 | "gulp-babel": "6.1.2", 87 | "gulp-load-plugins": "^1.2.0", 88 | "gulp-newer": "^1.1.0", 89 | "gulp-nodemon": "^2.0.6", 90 | "gulp-sourcemaps": "^1.6.0", 91 | "gulp-util": "^3.0.7", 92 | "isparta": "4.0.0", 93 | "mocha": "3.2.0", 94 | "run-sequence": "^1.1.5", 95 | "supertest": "2.0.1", 96 | "supertest-as-promised": "4.0.2", 97 | "validate-commit-msg": "^2.6.1" 98 | }, 99 | "license": "MIT", 100 | "config": { 101 | "ghooks": { 102 | "pre-commit": "yarn lint && yarn test" 103 | }, 104 | "commitizen": { 105 | "path": "./node_modules/cz-conventional-changelog" 106 | } 107 | }, 108 | "babel": { 109 | "presets": [ 110 | "es2015", 111 | "stage-3" 112 | ], 113 | "plugins": [ 114 | "add-module-exports", 115 | "transform-runtime" 116 | ] 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /server/modules/Client/controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const models = require('../../models/index'); 4 | 5 | /** 6 | * GET / 7 | * @param req 8 | * @param res 9 | * @param next 10 | */ 11 | async function list(req, res, next) { 12 | try { 13 | const data = await models.Client.findAll({}); 14 | res.json({success: true, data}); 15 | } catch (err) { 16 | next(err); 17 | } 18 | } 19 | 20 | 21 | /** 22 | * Get client 23 | * @returns {Client} 24 | */ 25 | async function get(req, res, next) { 26 | try { 27 | const data = await models.Client 28 | .find({ where: { id: req.params.clientId } }); 29 | res.json({ data }); 30 | } catch (err) { 31 | next(err); 32 | } 33 | } 34 | 35 | 36 | /** 37 | * Get client 38 | * @returns {Client} 39 | */ 40 | async function load(req, res, next, id) { 41 | try { 42 | const data = await models.Client 43 | .find({ where: { id: req.params.clientId } }); 44 | res.json({success: true, data}); 45 | } catch (err) { 46 | next(err); 47 | } 48 | } 49 | 50 | 51 | /** 52 | * Create new client 53 | * @property {string} req.body.name - The name of client. 54 | * @property {string} req.body.lastName - The lastName of client. 55 | * @property {string} req.body.rg - The rg of client. 56 | * @property {string} req.body.cpf - The cpf of client. 57 | * @property {string} req.body.maritalStatus - The maritalStatus of client. 58 | * @property {string} req.body.sex - The sex of client. 59 | * @property {string} req.body.address - The address of client. 60 | * @property {string} req.body.city - The city of client. 61 | * @property {string} req.body.state - The state of client. 62 | * @property {string} req.body.phone - The phone of client. 63 | * @property {string} req.body.facebook - The facebook of client. 64 | * @property {string} req.body.email - The email of client. 65 | * @property {string} req.body.birthday - The birthday of client. 66 | * @property {string} req.body.info - The info of client. 67 | * @returns {Client} 68 | */ 69 | async function create(req, res, next) { 70 | try { 71 | const client = { 72 | name: req.body.name, 73 | lastName: req.body.lastName, 74 | rg: req.body.rg, 75 | cpf: req.body.name, 76 | maritalStatus: req.body.maritalStatus, 77 | sex: req.body.sex, 78 | address: req.body.address, 79 | city: req.body.name, 80 | state: req.body.state, 81 | phone: req.body.phone, 82 | facebook: req.body.facebook, 83 | email: req.body.email, 84 | birthday: req.body.birthday, 85 | info: req.body.info 86 | }; 87 | const data = await models.Client.create(client); 88 | res.json({success: true, data}); 89 | } catch (err) { 90 | next(err); 91 | } 92 | } 93 | 94 | 95 | /** 96 | * Update existing client 97 | * @property {string} req.body.name - The name of client. 98 | * @property {string} req.body.lastName - The lastName of client. 99 | * @property {string} req.body.rg - The rg of client. 100 | * @property {string} req.body.cpf - The cpf of client. 101 | * @property {string} req.body.maritalStatus - The maritalStatus of client. 102 | * @property {string} req.body.sex - The sex of client. 103 | * @property {string} req.body.address - The address of client. 104 | * @property {string} req.body.city - The city of client. 105 | * @property {string} req.body.state - The state of client. 106 | * @property {string} req.body.phone - The phone of client. 107 | * @property {string} req.body.facebook - The facebook of client. 108 | * @property {string} req.body.email - The email of client. 109 | * @property {string} req.body.birthday - The birthday of client. 110 | * @property {string} req.body.info - The info of client. 111 | * @returns {Client} 112 | */ 113 | async function update(req, res, next) { 114 | try { 115 | const client = await models.Client 116 | .find({ where: { id: req.params.clientId } }); 117 | if(client){ 118 | const data = await client 119 | .updateAttributes({ 120 | name : req.body.name, 121 | lastName : req.body.lastName, 122 | rg : req.body.rg, 123 | cpf : req.body.cpf, 124 | maritalStatus : req.body.maritalStatus, 125 | sex : req.body.sex, 126 | city : req.body.city, 127 | state : req.body.state, 128 | phone : req.body.phone, 129 | facebook : req.body.facebook, 130 | email : req.body.email, 131 | birthday : req.body.birthday, 132 | info : req.body.info 133 | }); 134 | res.json({success: true, data}); 135 | } 136 | } catch (err) { 137 | next(err); 138 | } 139 | } 140 | 141 | 142 | /** 143 | * Delete client. 144 | * @returns {Client} 145 | */ 146 | async function remove(req, res, next) { 147 | try { 148 | const data = await models.Client 149 | .destroy({ where: { id: req.params.clientId } }); 150 | res.json({success: true, data}); 151 | } catch (err) { 152 | next(err); 153 | } 154 | } 155 | 156 | 157 | /** 158 | * Load schedules and append to req. 159 | */ 160 | async function getSchedulesByClientId(req, res, next) { 161 | try { 162 | const data = await models.Schedule 163 | .findAll({ where: { clientId: req.params.clientId } }); 164 | res.json({success: true, data}); 165 | } catch (err) { 166 | next(err); 167 | } 168 | } 169 | 170 | 171 | /** 172 | * Create new schedule 173 | * @property {string} req.body.description - The schedule description of client. 174 | * @returns {Client} 175 | */ 176 | async function createSchedule(req, res, next) { 177 | try { 178 | const schedule = { 179 | title: req.body.title, 180 | clientId: req.params.clientId, 181 | start: req.body.start, 182 | end: req.body.end, 183 | price: req.body.price 184 | }; 185 | 186 | const data = await models.Schedule.create(schedule); 187 | res.json({success: true, data }); 188 | } catch (err) { 189 | next(err); 190 | } 191 | } 192 | 193 | 194 | /** 195 | * Delete schedule. 196 | * @returns {Schedule} 197 | */ 198 | async function removeSchedule(req, res, next) { 199 | try { 200 | const data = await models.Schedule 201 | .destroy({ where: { id: req.params.scheduleId } }); 202 | res.json({success: true, data}); 203 | } catch (err) { 204 | next(err); 205 | } 206 | } 207 | 208 | 209 | 210 | export { load, list, get, create, update, remove, removeSchedule, createSchedule, getSchedulesByClientId }; 211 | --------------------------------------------------------------------------------