├── .gitignore ├── README.md ├── Routes ├── users.routes.js └── admin.routes.js ├── Middleware └── checkAuth.middleware.js ├── Models ├── user.js └── admin.js ├── package.json ├── LICENSE ├── app.js └── Controllers ├── users.controllers.js └── admin.controllers.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.Js-sample-project-structure 2 | Originally written for Medium article but can be used as a sample boilerplate as well 3 | 4 | Medium article: [Link](https://medium.com/codechef-vit/a-better-project-structure-with-express-and-node-js-c23abc2d736f) 5 | -------------------------------------------------------------------------------- /Routes/users.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const checkAuth = require('../Middleware/checkAuth.middleware'); 3 | const userControllers = require('../Controllers/users.controllers'); 4 | const router = express.Router(); 5 | 6 | router.post('/signup', userControllers.userRegister); 7 | router.post('/login', userControllers.userLogin); 8 | router.get('/me', checkAuth, userControllers.getMe); 9 | 10 | module.exports = router -------------------------------------------------------------------------------- /Routes/admin.routes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const checkAuth = require('../Middleware/checkAuth.middleware'); 3 | const adminControllers = require('../Controllers/admin.controllers'); 4 | const router = express.Router(); 5 | 6 | router.post('/signup', adminControllers.adminRegister); 7 | router.post('/login', adminControllers.adminLogin); 8 | router.get('/me', checkAuth, adminControllers.getMe); 9 | 10 | module.exports = router -------------------------------------------------------------------------------- /Middleware/checkAuth.middleware.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | 3 | module.exports = function (req, res, next) { 4 | const token = req.header("auth-token"); 5 | if (!token) return res.status(400).send("Access Denied!, no token entered"); 6 | 7 | try { 8 | const verified = jwt.verify(token, process.env.jwtSecret); 9 | req.user = verified; 10 | next(); 11 | } catch (err) { 12 | res.status(400).send({ error: "auth failed, check auth-token222" }); 13 | } 14 | }; -------------------------------------------------------------------------------- /Models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const userSchema = new mongoose.Schema({ 4 | _id: mongoose.Schema.Types.ObjectId, 5 | name: String, 6 | email: { 7 | type: String, 8 | lowercase: true, 9 | match: /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/, 10 | }, 11 | password: String, 12 | phone_number: Number, 13 | }); 14 | 15 | module.exports = mongoose.model("User", userSchema); -------------------------------------------------------------------------------- /Models/admin.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const adminSchema = new mongoose.Schema({ 4 | _id: mongoose.Schema.Types.ObjectId, 5 | name: String, 6 | email: { 7 | type: String, 8 | lowercase: true, 9 | match: /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/, 10 | }, 11 | password: String, 12 | phone_number: Number, 13 | }); 14 | 15 | module.exports = mongoose.model("Admin", adminSchema); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-coding-practices", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcrypt": "^5.0.0", 10 | "body-parser": "^1.19.0", 11 | "cookie-parser": "~1.4.4", 12 | "debug": "~2.6.9", 13 | "express": "~4.16.1", 14 | "http-errors": "~1.6.3", 15 | "jade": "~1.11.0", 16 | "jsonwebtoken": "^8.5.1", 17 | "mongoose": "^5.10.9", 18 | "morgan": "~1.9.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jugal D. Bhatt 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 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var logger = require('morgan'); 3 | const mongoose = require('mongoose'); 4 | const bodyParser = require('body-parser') 5 | 6 | var usersRouter = require('./Routes/users.routes'); 7 | var adminRouter = require('./Routes/admin.routes'); 8 | 9 | var app = express(); 10 | 11 | app.use(logger('dev')); 12 | app.use(bodyParser.urlencoded({ extended: false })); 13 | app.use(bodyParser.json()); 14 | 15 | const dbURI = process.env.dbURI; 16 | 17 | mongoose 18 | .connect(dbURI, { 19 | useNewUrlParser: true, 20 | useCreateIndex: true, 21 | useUnifiedTopology: true, 22 | }) 23 | .then(() => console.log("Database Connected")) 24 | .catch((err) => console.log(err)); 25 | 26 | mongoose.Promise = global.Promise; 27 | 28 | app.use('/users', usersRouter); 29 | app.use('/admin', adminRouter) 30 | 31 | // catch 404 and forward to error handler 32 | app.use(function(req, res, next) { 33 | res.status(404).json({ 34 | message: "No such route exists" 35 | }) 36 | }); 37 | 38 | // error handler 39 | app.use(function(err, req, res, next) { 40 | res.status(err.status || 500).json({ 41 | message: "Error Message" 42 | }) 43 | }); 44 | 45 | module.exports = app; 46 | -------------------------------------------------------------------------------- /Controllers/users.controllers.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcrypt"); 2 | require("../Node.Js-sample-project-structure/node_modules/dotenv").config(); 3 | const jwt = require("jsonwebtoken"); 4 | const User = require("../Models/user"); 5 | 6 | 7 | const userRegister = (req, res, next) => { 8 | User.find({ email: req.body.email }) 9 | .exec() 10 | .then((user) => { 11 | if (user.length >= 1) { 12 | res.status(409).json({ 13 | message:"Email Exists" 14 | }) 15 | } else { 16 | bcrypt.hash(req.body.password, 10, (err, hash) => { 17 | if (err) { 18 | return res.status(500).json({ 19 | error: err, 20 | }); 21 | } else { 22 | const user = new User({ 23 | _id: new mongoose.Types.ObjectId(), 24 | email: req.body.email, 25 | password: hash, 26 | name: req.body.name, 27 | phone_number: req.body.phone_number 28 | }); 29 | user 30 | .save() 31 | .then(async (result) => { 32 | await result 33 | .save() 34 | .then((result1) => { 35 | console.log(`User created ${result}`) 36 | res.status(201).json({ 37 | userDetails: { 38 | userId: result._id, 39 | email: result.email, 40 | name: result.name, 41 | phone_number: result.phone_number, 42 | }, 43 | }) 44 | }) 45 | .catch((err) => { 46 | console.log(err) 47 | res.status(400).json({ 48 | message: err.toString() 49 | }) 50 | }); 51 | }) 52 | .catch((err) => { 53 | console.log(err) 54 | res.status(500).json({ 55 | message: err.toString() 56 | }) 57 | }); 58 | } 59 | }); 60 | } 61 | }) 62 | .catch((err) => { 63 | console.log(err) 64 | res.status(500).json({ 65 | message: err.toString() 66 | }) 67 | }); 68 | } 69 | 70 | 71 | const userLogin = (req, res, next) => { 72 | User.find({ email: req.body.email }) 73 | .exec() 74 | .then((user) => { 75 | console.log(user) 76 | if (user.length < 1) { 77 | return res.status(401).json({ 78 | message: "Auth failed: Email not found probably", 79 | }); 80 | } 81 | bcrypt.compare(req.body.password, user[0].password, (err, result) => { 82 | if (err) { 83 | console.log(err) 84 | return res.status(401).json({ 85 | message: "Auth failed", 86 | }); 87 | } 88 | if (result) { 89 | const token = jwt.sign( 90 | { 91 | userId: user[0]._id, 92 | email: user[0].email, 93 | name: user[0].name, 94 | phone_number: user[0].phone_number, 95 | }, 96 | process.env.jwtSecret, 97 | { 98 | expiresIn: "1d", 99 | } 100 | ); 101 | console.log(user[0]) 102 | return res.status(200).json({ 103 | message: "Auth successful", 104 | userDetails: { 105 | userId: user[0]._id, 106 | name: user[0].name, 107 | email: user[0].email, 108 | phone_number: user[0].phone_number, 109 | }, 110 | token: token, 111 | }); 112 | } 113 | res.status(401).json({ 114 | message: "Auth failed1", 115 | }); 116 | }); 117 | }) 118 | .catch((err) => { 119 | res.status(500).json({ 120 | error: err, 121 | }); 122 | }); 123 | } 124 | 125 | const getMe = async (req, res) => { 126 | const userId = req.user.userId; 127 | const user = await User.findById(userId); 128 | if (user) { 129 | res.status(200).json({ 130 | message: "Found", 131 | user, 132 | }); 133 | } else { 134 | res.status(400).json({ 135 | message: "Bad request", 136 | }); 137 | } 138 | }; 139 | 140 | module.exports = { 141 | userLogin, 142 | userRegister, 143 | getMe, 144 | }; 145 | -------------------------------------------------------------------------------- /Controllers/admin.controllers.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcrypt"); 2 | require("../Node.Js-sample-project-structure/node_modules/dotenv").config(); 3 | const jwt = require("jsonwebtoken"); 4 | const Admin = require("../Node.Js-sample-project-structure/Models/admin"); 5 | 6 | 7 | const adminRegister = (req, res, next) => { 8 | Admin.find({ email: req.body.email }) 9 | .exec() 10 | .then((admin) => { 11 | if (admin.length >= 1) { 12 | res.status(409).json({ 13 | message:"Email Exists" 14 | }) 15 | } else { 16 | bcrypt.hash(req.body.password, 10, (err, hash) => { 17 | if (err) { 18 | return res.status(500).json({ 19 | error: err, 20 | }); 21 | } else { 22 | const admin = new Admin({ 23 | _id: new mongoose.Types.ObjectId(), 24 | email: req.body.email, 25 | password: hash, 26 | name: req.body.name, 27 | phone_number: req.body.phone_number 28 | }); 29 | admin 30 | .save() 31 | .then(async (result) => { 32 | await result 33 | .save() 34 | .then((result1) => { 35 | console.log(`admin created ${result}`) 36 | res.status(201).json({ 37 | adminDetails: { 38 | adminId: result._id, 39 | email: result.email, 40 | name: result.name, 41 | phone_number: result.phone_number, 42 | }, 43 | }) 44 | }) 45 | .catch((err) => { 46 | console.log(err) 47 | res.status(400).json({ 48 | message: err.toString() 49 | }) 50 | }); 51 | }) 52 | .catch((err) => { 53 | console.log(err) 54 | res.status(500).json({ 55 | message: err.toString() 56 | }) 57 | }); 58 | } 59 | }); 60 | } 61 | }) 62 | .catch((err) => { 63 | console.log(err) 64 | res.status(500).json({ 65 | message: err.toString() 66 | }) 67 | }); 68 | } 69 | 70 | 71 | const adminLogin = (req, res, next) => { 72 | Admin.find({ email: req.body.email }) 73 | .exec() 74 | .then((admin) => { 75 | console.log(admin) 76 | if (admin.length < 1) { 77 | return res.status(401).json({ 78 | message: "Auth failed: Email not found probably", 79 | }); 80 | } 81 | bcrypt.compare(req.body.password, admin[0].password, (err, result) => { 82 | if (err) { 83 | console.log(err) 84 | return res.status(401).json({ 85 | message: "Auth failed", 86 | }); 87 | } 88 | if (result) { 89 | const token = jwt.sign( 90 | { 91 | adminId: admin[0]._id, 92 | email: admin[0].email, 93 | name: admin[0].name, 94 | phone_number: admin[0].phone_number, 95 | }, 96 | process.env.jwtSecret, 97 | { 98 | expiresIn: "1d", 99 | } 100 | ); 101 | console.log(admin[0]) 102 | return res.status(200).json({ 103 | message: "Auth successful", 104 | adminDetails: { 105 | adminId: admin[0]._id, 106 | name: admin[0].name, 107 | email: admin[0].email, 108 | phone_number: admin[0].phone_number, 109 | }, 110 | token: token, 111 | }); 112 | } 113 | res.status(401).json({ 114 | message: "Auth failed1", 115 | }); 116 | }); 117 | }) 118 | .catch((err) => { 119 | res.status(500).json({ 120 | error: err, 121 | }); 122 | }); 123 | } 124 | 125 | const getMe = async (req, res) => { 126 | const adminId = req.admin.adminId; 127 | const admin = await Admin.findById(adminId); 128 | if (admin) { 129 | res.status(200).json({ 130 | message: "Found", 131 | admin, 132 | }); 133 | } else { 134 | res.status(400).json({ 135 | message: "Bad request", 136 | }); 137 | } 138 | }; 139 | 140 | module.exports = { 141 | adminLogin, 142 | adminRegister, 143 | getMe, 144 | }; 145 | --------------------------------------------------------------------------------