├── README.md ├── package.json ├── server.js └── server ├── config ├── app.js ├── db.js └── env_config │ ├── config.js │ └── local.js ├── controllers └── apis │ └── v1 │ ├── auth.js │ └── users.js ├── middlewares ├── authGaurd.js └── validation.js ├── models └── user.js ├── routes ├── apis │ ├── index.js │ └── v1.js └── index.js └── services └── v1 ├── auth └── auth.js └── users └── users.js /README.md: -------------------------------------------------------------------------------- 1 | # Secure_RESTfulAPI 2 | This is a secure RESTful API using node.js,express.js and JWT authentication 3 | 4 | Clone this repository and run npm install to start the app 5 | 6 | You can find full tutorials on www.codesquery.com 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure_restapi_demo", 3 | "version": "1.0.0", 4 | "description": "simple nodejs rest api with authentication", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "restapi", 11 | "secure", 12 | "restapi", 13 | "nodejs", 14 | "rest", 15 | "api", 16 | "authenticate", 17 | "restapi" 18 | ], 19 | "author": "sachin jaiswal", 20 | "license": "ISC", 21 | "dependencies": { 22 | "bcryptjs": "^2.4.3", 23 | "body-parser": "^1.18.3", 24 | "express": "^4.16.4", 25 | "express-validator": "^5.3.1", 26 | "jsonwebtoken": "^8.4.0", 27 | "mongoose": "^5.4.13" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const server = require('./server/config/app')(); 2 | const config = require('./server/config/env_config/config'); 3 | const db = require('./server/config/db'); 4 | 5 | //create the basic server setup 6 | server.create(config, db); 7 | 8 | //start the server 9 | server.start(); -------------------------------------------------------------------------------- /server/config/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const mongoose = require('mongoose'); 4 | const expressValidator = require('express-validator') 5 | 6 | module.exports = function () { 7 | let server = express(), 8 | create, 9 | start; 10 | 11 | create = (config, db) => { 12 | let routes = require('../routes'); 13 | // set all the server things 14 | server.set('env', config.env); 15 | server.set('port', config.port); 16 | server.set('hostname', config.hostname); 17 | 18 | // add middleware to parse the json 19 | server.use(bodyParser.json()); 20 | server.use(expressValidator()) 21 | server.use(bodyParser.urlencoded({ 22 | extended: false 23 | })); 24 | 25 | //connect the database 26 | mongoose.connect( 27 | db.database, 28 | { 29 | useNewUrlParser: true, 30 | useCreateIndex: true 31 | } 32 | ); 33 | 34 | // Set up routes 35 | routes.init(server); 36 | }; 37 | 38 | 39 | start = () => { 40 | let hostname = server.get('hostname'), 41 | port = server.get('port'); 42 | server.listen(port, function () { 43 | console.log('Express server listening on - http://' + hostname + ':' + port); 44 | }); 45 | }; 46 | return { 47 | create: create, 48 | start: start 49 | }; 50 | }; -------------------------------------------------------------------------------- /server/config/db.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'database': 'mongodb://127.0.0.1:27017/restAPI' 3 | }; -------------------------------------------------------------------------------- /server/config/env_config/config.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const env = process.env.NODE_ENV || 'local'; 3 | const envConfig = require('./' + env); 4 | let defaultConfig = { 5 | env: env 6 | }; 7 | module.exports = _.merge(defaultConfig, envConfig); -------------------------------------------------------------------------------- /server/config/env_config/local.js: -------------------------------------------------------------------------------- 1 | let localConfig = { 2 | hostname: 'localhost', 3 | port: 3000, 4 | secret : 'restapisecret', 5 | }; 6 | 7 | module.exports = localConfig; -------------------------------------------------------------------------------- /server/controllers/apis/v1/auth.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const authService = require('../../../services/v1/auth/auth'); 3 | const validation = require('../../../middlewares/validation'); 4 | let router = express.Router(); 5 | 6 | router.post('/register', validation.validateRegistrationBody(), authService.register); 7 | 8 | router.post('/login', validation.validateLoginBody(), authService.login); 9 | 10 | module.exports = router; -------------------------------------------------------------------------------- /server/controllers/apis/v1/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const userService = require('../../../services/v1/users/users'); 3 | const authClientRequest = require('../../../middlewares/authGaurd'); 4 | let router = express.Router(); 5 | 6 | router.get('/:userId', authClientRequest.authClientToken ,userService.getUserDetails); 7 | 8 | module.exports = router; -------------------------------------------------------------------------------- /server/middlewares/authGaurd.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const jwt = require('jsonwebtoken'); 3 | const config = require('../config/env_config/config'); 4 | 5 | const authClientToken = async (req,res,next) => { 6 | 7 | let token = req.headers['x-access-token']; 8 | 9 | if (!token){ 10 | return res.status(401).json({ 11 | "errors" : [{ 12 | "msg" : " No token provided" 13 | }] 14 | }); 15 | } 16 | 17 | jwt.verify(token,config.secret , (err,decoded) => { 18 | if(err){ 19 | return res.status(401).json({ 20 | "errors" : [{ 21 | "msg" : "Invalid Token" 22 | }] 23 | }); 24 | } 25 | 26 | return next(); 27 | }); 28 | } 29 | 30 | module.exports = { 31 | authClientToken : authClientToken 32 | } -------------------------------------------------------------------------------- /server/middlewares/validation.js: -------------------------------------------------------------------------------- 1 | const { body } = require('express-validator/check') 2 | 3 | const validateRegistrationBody = () => { 4 | return [ 5 | body('name') 6 | .exists() 7 | .withMessage('name field is required') 8 | .isLength({min:3}) 9 | .withMessage('name must be greater than 3 letters'), 10 | body('email').exists() 11 | .withMessage('email field is required') 12 | .isEmail() 13 | .withMessage('Email is invalid'), 14 | body('password') 15 | .exists() 16 | .withMessage('password field is required') 17 | .isLength({min : 8,max: 12}) 18 | .withMessage('password must be in between 8 to 12 characters long') 19 | ] 20 | } 21 | 22 | const validateLoginBody = () => { 23 | return [ 24 | body('email').exists() 25 | .withMessage('email field is required') 26 | .isEmail() 27 | .withMessage('Email is invalid'), 28 | body('password') 29 | .exists() 30 | .withMessage('password field is required') 31 | .isLength({min : 8,max: 12}) 32 | .withMessage('password must be in between 8 to 12 characters long') 33 | ] 34 | } 35 | 36 | module.exports = { 37 | validateRegistrationBody : validateRegistrationBody, 38 | validateLoginBody : validateLoginBody 39 | } -------------------------------------------------------------------------------- /server/models/user.js: -------------------------------------------------------------------------------- 1 | let mongoose = require('mongoose'); 2 | let Schema = mongoose.Schema; 3 | 4 | var User = new Schema({ 5 | name: { 6 | type: String, 7 | required : [ true, 'name is required'], 8 | lowercase : true 9 | }, 10 | email: { 11 | type: String, 12 | required : [ true, 'email is required'], 13 | unique : true, 14 | lowercase : true 15 | }, 16 | password: { 17 | type: String, 18 | required : [ true, 'password is required'] 19 | } 20 | }, { 21 | timestamps: true 22 | }); 23 | 24 | module.exports = mongoose.model('User', User); -------------------------------------------------------------------------------- /server/routes/apis/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const v1ApiController = require('./v1'); 3 | let router = express.Router(); 4 | router.use('/v1', v1ApiController); 5 | module.exports = router; -------------------------------------------------------------------------------- /server/routes/apis/v1.js: -------------------------------------------------------------------------------- 1 | const userController = require('../../controllers/apis/v1/users'); 2 | const authController = require('../../controllers/apis/v1/auth'); 3 | 4 | const express = require('express'); 5 | let router = express.Router(); 6 | router.use('/users', userController); 7 | router.use('/auth', authController); 8 | module.exports = router; -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | const apiRoute = require('./apis'); 2 | 3 | const init = (server) => { 4 | server.get('*', function (req, res, next) { 5 | console.log('Request was made to: ' + req.originalUrl); 6 | return next(); 7 | }); 8 | 9 | server.use('/api', apiRoute); 10 | } 11 | module.exports = { 12 | init: init 13 | }; -------------------------------------------------------------------------------- /server/services/v1/auth/auth.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bcrypt = require('bcryptjs'); 3 | const jwt = require('jsonwebtoken') 4 | const { validationResult } = require('express-validator/check'); 5 | 6 | const config = require('../../../config/env_config/config'); 7 | const userModel = require('../../../models/user'); 8 | 9 | const register = async (req,res,next) => { 10 | const errors = validationResult(req); 11 | 12 | if(!errors.isEmpty()){ 13 | return res.status(422).json({ errors: errors.array() }); 14 | } 15 | 16 | let { name, email , password } = req.body; 17 | 18 | let isEmailExists = await userModel.findOne({"email" : email}); 19 | 20 | if(isEmailExists){ 21 | return res.status(409).json({ 22 | "errors" : [{ 23 | "msg" : "email already exists" 24 | }] 25 | }) 26 | } 27 | 28 | let hashedPassword = await bcrypt.hash(password, 8); 29 | 30 | try{ 31 | let user = await userModel.create({ 32 | name : name, 33 | email: email, 34 | password : hashedPassword 35 | }); 36 | 37 | if(!user){ 38 | throw new error(); 39 | } 40 | 41 | return res.status(201).json({ 42 | "success" : [{ 43 | "msg" : "user registered successfully" 44 | }] 45 | }); 46 | }catch(error){ 47 | console.log(error); 48 | return res.status(500).json( 49 | { 50 | "errors" : [{ 51 | "msg": "there was a problem registering a user." 52 | }] 53 | } 54 | ); 55 | } 56 | 57 | 58 | } 59 | 60 | const login = async (req,res,next) => { 61 | 62 | const errors = validationResult(req); 63 | 64 | if(!errors.isEmpty()){ 65 | return res.status(422).json({ errors: errors.array() }); 66 | } 67 | 68 | let { email, password } = req.body 69 | 70 | try{ 71 | let isUserExists = await userModel.findOne({"email" : email}); 72 | 73 | let isPasswordValid = await bcrypt.compare(password, isUserExists.password); 74 | 75 | if(!isUserExists || !isPasswordValid){ 76 | return res.status(401).json({ 77 | "errors" : [{ 78 | "msg" : "email/password is wrong" 79 | }] 80 | }) 81 | } 82 | 83 | let token = jwt.sign({ id: isUserExists._id }, config.secret, { expiresIn: 86400 }); 84 | 85 | res.status(200).json({ 86 | "success" : [{ 87 | "msg" : "user login successfully", 88 | "email" : email, 89 | "token" : token 90 | }] 91 | }) 92 | }catch(error){ 93 | console.log(error); 94 | return res.status(500).json( 95 | { 96 | "errors" : [{ 97 | "msg": "there was a problem login a user." 98 | }] 99 | } 100 | ); 101 | } 102 | 103 | } 104 | 105 | module.exports = { 106 | register : register, 107 | login : login 108 | } -------------------------------------------------------------------------------- /server/services/v1/users/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const userModel = require('../../../models/user'); 3 | 4 | const getUserDetails = async (req,res,next) => { 5 | 6 | let { userId } = req.params; 7 | 8 | let user = await userModel.findById(userId).select('name , email'); 9 | 10 | if(!user){ 11 | return res.status(404).json({ 12 | "errors":[{ 13 | "msg" : " no user found" 14 | }] 15 | }) 16 | } 17 | 18 | return res.status(200).json({ 19 | "success" : [{ 20 | "msg" : " user fetched successfully", 21 | "data" : user 22 | }] 23 | }) 24 | } 25 | 26 | module.exports = { 27 | getUserDetails : getUserDetails 28 | } --------------------------------------------------------------------------------