├── .circleci └── config.yml ├── .gitignore ├── README.md ├── app.js ├── auth ├── AuthController.js └── VerifyToken.js ├── config.js ├── db.js ├── package-lock.json ├── package.json ├── server.js └── user ├── User.js └── UserController.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:7.10 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/code/securing-restful-apis-with-jwt 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: npm install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | # - run: yarn test 38 | 39 | 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Securing RESTful APIs with JWT 2 | 3 | ![]() 4 | 5 | How to secure a Nodejs RESTful CRUD API using JSON web tokens? 6 | 7 | This tutorial will demo how to use JWT with an existing bare bones 8 | API using mongodb as the database. 9 | 10 | It consist of a User model and controller. The model 11 | defines the data, and the controller will contain all 12 | the business logic needed to interact with the database. 13 | 14 | It has a db file which will be used to 15 | connect the app to the database, and an app file used 16 | for bootstrapping the application itself. 17 | 18 | The server file is used to spin up the server and tells the 19 | app to listen on a specific port. 20 | 21 | The auth folder contains the configuration for registering and 22 | logging users in, signing and verifying tokens. 23 | 24 | Follow the tutorial for this repository at https://medium.freecodecamp.org/securing-node-js-restful-apis-with-json-web-tokens-9f811a92bb52 25 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var db = require('./db'); 4 | global.__root = __dirname + '/'; 5 | 6 | app.get('/api', function (req, res) { 7 | res.status(200).send('API works.'); 8 | }); 9 | 10 | var UserController = require(__root + 'user/UserController'); 11 | app.use('/api/users', UserController); 12 | 13 | var AuthController = require(__root + 'auth/AuthController'); 14 | app.use('/api/auth', AuthController); 15 | 16 | module.exports = app; -------------------------------------------------------------------------------- /auth/AuthController.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var bodyParser = require('body-parser'); 4 | 5 | var VerifyToken = require('./VerifyToken'); 6 | 7 | router.use(bodyParser.urlencoded({ extended: false })); 8 | router.use(bodyParser.json()); 9 | var User = require('../user/User'); 10 | 11 | /** 12 | * Configure JWT 13 | */ 14 | var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens 15 | var bcrypt = require('bcryptjs'); 16 | var config = require('../config'); // get config file 17 | 18 | router.post('/login', function(req, res) { 19 | 20 | User.findOne({ email: req.body.email }, function (err, user) { 21 | if (err) return res.status(500).send('Error on the server.'); 22 | if (!user) return res.status(404).send('No user found.'); 23 | 24 | // check if the password is valid 25 | var passwordIsValid = bcrypt.compareSync(req.body.password, user.password); 26 | if (!passwordIsValid) return res.status(401).send({ auth: false, token: null }); 27 | 28 | // if user is found and password is valid 29 | // create a token 30 | var token = jwt.sign({ id: user._id }, config.secret, { 31 | expiresIn: 86400 // expires in 24 hours 32 | }); 33 | 34 | // return the information including token as JSON 35 | res.status(200).send({ auth: true, token: token }); 36 | }); 37 | 38 | }); 39 | 40 | router.get('/logout', function(req, res) { 41 | res.status(200).send({ auth: false, token: null }); 42 | }); 43 | 44 | router.post('/register', function(req, res) { 45 | 46 | var hashedPassword = bcrypt.hashSync(req.body.password, 8); 47 | 48 | User.create({ 49 | name : req.body.name, 50 | email : req.body.email, 51 | password : hashedPassword 52 | }, 53 | function (err, user) { 54 | if (err) return res.status(500).send("There was a problem registering the user`."); 55 | 56 | // if user is registered without errors 57 | // create a token 58 | var token = jwt.sign({ id: user._id }, config.secret, { 59 | expiresIn: 86400 // expires in 24 hours 60 | }); 61 | 62 | res.status(200).send({ auth: true, token: token }); 63 | }); 64 | 65 | }); 66 | 67 | router.get('/me', VerifyToken, function(req, res, next) { 68 | 69 | User.findById(req.userId, { password: 0 }, function (err, user) { 70 | if (err) return res.status(500).send("There was a problem finding the user."); 71 | if (!user) return res.status(404).send("No user found."); 72 | res.status(200).send(user); 73 | }); 74 | 75 | }); 76 | 77 | module.exports = router; -------------------------------------------------------------------------------- /auth/VerifyToken.js: -------------------------------------------------------------------------------- 1 | var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens 2 | var config = require('../config'); // get our config file 3 | 4 | function verifyToken(req, res, next) { 5 | 6 | // check header or url parameters or post parameters for token 7 | var token = req.headers['x-access-token']; 8 | if (!token) 9 | return res.status(403).send({ auth: false, message: 'No token provided.' }); 10 | 11 | // verifies secret and checks exp 12 | jwt.verify(token, config.secret, function(err, decoded) { 13 | if (err) 14 | return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' }); 15 | 16 | // if everything is good, save to request for use in other routes 17 | req.userId = decoded.id; 18 | next(); 19 | }); 20 | 21 | } 22 | 23 | module.exports = verifyToken; -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'secret': 'supersecret' 3 | }; -------------------------------------------------------------------------------- /db.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | mongoose.connect('mongodb://127.0.0.1:27017/securing-rest-apis-with-jwt', { useMongoClient: true }); -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-creating-restful-apis", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "async": { 22 | "version": "2.1.4", 23 | "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", 24 | "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", 25 | "requires": { 26 | "lodash": "4.17.5" 27 | } 28 | }, 29 | "base64url": { 30 | "version": "2.0.0", 31 | "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", 32 | "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" 33 | }, 34 | "bcryptjs": { 35 | "version": "2.4.3", 36 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", 37 | "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" 38 | }, 39 | "bluebird": { 40 | "version": "3.5.0", 41 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", 42 | "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" 43 | }, 44 | "body-parser": { 45 | "version": "1.18.2", 46 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 47 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 48 | "requires": { 49 | "bytes": "3.0.0", 50 | "content-type": "1.0.4", 51 | "debug": "2.6.9", 52 | "depd": "1.1.2", 53 | "http-errors": "1.6.3", 54 | "iconv-lite": "0.4.19", 55 | "on-finished": "2.3.0", 56 | "qs": "6.5.1", 57 | "raw-body": "2.3.2", 58 | "type-is": "1.6.16" 59 | } 60 | }, 61 | "bson": { 62 | "version": "1.0.6", 63 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.6.tgz", 64 | "integrity": "sha512-D8zmlb46xfuK2gGvKmUjIklQEouN2nQ0LEHHeZ/NoHM2LDiMk2EYzZ5Ntw/Urk+bgMDosOZxaRzXxvhI5TcAVQ==" 65 | }, 66 | "buffer-equal-constant-time": { 67 | "version": "1.0.1", 68 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 69 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 70 | }, 71 | "buffer-shims": { 72 | "version": "1.0.0", 73 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", 74 | "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" 75 | }, 76 | "bytes": { 77 | "version": "3.0.0", 78 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 79 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 80 | }, 81 | "content-disposition": { 82 | "version": "0.5.2", 83 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 84 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 85 | }, 86 | "content-type": { 87 | "version": "1.0.4", 88 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 89 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 90 | }, 91 | "cookie": { 92 | "version": "0.3.1", 93 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 94 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 95 | }, 96 | "cookie-signature": { 97 | "version": "1.0.6", 98 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 99 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 100 | }, 101 | "core-util-is": { 102 | "version": "1.0.2", 103 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 104 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 105 | }, 106 | "debug": { 107 | "version": "2.6.9", 108 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 109 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 110 | "requires": { 111 | "ms": "2.0.0" 112 | } 113 | }, 114 | "depd": { 115 | "version": "1.1.2", 116 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 117 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 118 | }, 119 | "destroy": { 120 | "version": "1.0.4", 121 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 122 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 123 | }, 124 | "ecdsa-sig-formatter": { 125 | "version": "1.0.9", 126 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", 127 | "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", 128 | "requires": { 129 | "base64url": "2.0.0", 130 | "safe-buffer": "5.1.1" 131 | } 132 | }, 133 | "ee-first": { 134 | "version": "1.1.1", 135 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 136 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 137 | }, 138 | "encodeurl": { 139 | "version": "1.0.2", 140 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 141 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 142 | }, 143 | "es6-promise": { 144 | "version": "3.2.1", 145 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", 146 | "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" 147 | }, 148 | "escape-html": { 149 | "version": "1.0.3", 150 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 151 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 152 | }, 153 | "etag": { 154 | "version": "1.8.1", 155 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 156 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 157 | }, 158 | "express": { 159 | "version": "4.16.3", 160 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 161 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 162 | "requires": { 163 | "accepts": "1.3.5", 164 | "array-flatten": "1.1.1", 165 | "body-parser": "1.18.2", 166 | "content-disposition": "0.5.2", 167 | "content-type": "1.0.4", 168 | "cookie": "0.3.1", 169 | "cookie-signature": "1.0.6", 170 | "debug": "2.6.9", 171 | "depd": "1.1.2", 172 | "encodeurl": "1.0.2", 173 | "escape-html": "1.0.3", 174 | "etag": "1.8.1", 175 | "finalhandler": "1.1.1", 176 | "fresh": "0.5.2", 177 | "merge-descriptors": "1.0.1", 178 | "methods": "1.1.2", 179 | "on-finished": "2.3.0", 180 | "parseurl": "1.3.2", 181 | "path-to-regexp": "0.1.7", 182 | "proxy-addr": "2.0.3", 183 | "qs": "6.5.1", 184 | "range-parser": "1.2.0", 185 | "safe-buffer": "5.1.1", 186 | "send": "0.16.2", 187 | "serve-static": "1.13.2", 188 | "setprototypeof": "1.1.0", 189 | "statuses": "1.4.0", 190 | "type-is": "1.6.16", 191 | "utils-merge": "1.0.1", 192 | "vary": "1.1.2" 193 | }, 194 | "dependencies": { 195 | "statuses": { 196 | "version": "1.4.0", 197 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 198 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 199 | } 200 | } 201 | }, 202 | "finalhandler": { 203 | "version": "1.1.1", 204 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 205 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 206 | "requires": { 207 | "debug": "2.6.9", 208 | "encodeurl": "1.0.2", 209 | "escape-html": "1.0.3", 210 | "on-finished": "2.3.0", 211 | "parseurl": "1.3.2", 212 | "statuses": "1.4.0", 213 | "unpipe": "1.0.0" 214 | }, 215 | "dependencies": { 216 | "statuses": { 217 | "version": "1.4.0", 218 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 219 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 220 | } 221 | } 222 | }, 223 | "forwarded": { 224 | "version": "0.1.2", 225 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 226 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 227 | }, 228 | "fresh": { 229 | "version": "0.5.2", 230 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 231 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 232 | }, 233 | "hoek": { 234 | "version": "2.16.3", 235 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", 236 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" 237 | }, 238 | "hooks-fixed": { 239 | "version": "2.0.2", 240 | "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz", 241 | "integrity": "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ==" 242 | }, 243 | "http-errors": { 244 | "version": "1.6.3", 245 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 246 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 247 | "requires": { 248 | "depd": "1.1.2", 249 | "inherits": "2.0.3", 250 | "setprototypeof": "1.1.0", 251 | "statuses": "1.5.0" 252 | } 253 | }, 254 | "iconv-lite": { 255 | "version": "0.4.19", 256 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 257 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 258 | }, 259 | "inherits": { 260 | "version": "2.0.3", 261 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 262 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 263 | }, 264 | "ipaddr.js": { 265 | "version": "1.6.0", 266 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 267 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 268 | }, 269 | "isarray": { 270 | "version": "1.0.0", 271 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 272 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 273 | }, 274 | "isemail": { 275 | "version": "1.2.0", 276 | "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", 277 | "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" 278 | }, 279 | "joi": { 280 | "version": "6.10.1", 281 | "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", 282 | "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", 283 | "requires": { 284 | "hoek": "2.16.3", 285 | "isemail": "1.2.0", 286 | "moment": "2.22.0", 287 | "topo": "1.1.0" 288 | } 289 | }, 290 | "jsonwebtoken": { 291 | "version": "7.4.3", 292 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz", 293 | "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=", 294 | "requires": { 295 | "joi": "6.10.1", 296 | "jws": "3.1.4", 297 | "lodash.once": "4.1.1", 298 | "ms": "2.0.0", 299 | "xtend": "4.0.1" 300 | } 301 | }, 302 | "jwa": { 303 | "version": "1.1.5", 304 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", 305 | "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", 306 | "requires": { 307 | "base64url": "2.0.0", 308 | "buffer-equal-constant-time": "1.0.1", 309 | "ecdsa-sig-formatter": "1.0.9", 310 | "safe-buffer": "5.1.1" 311 | } 312 | }, 313 | "jws": { 314 | "version": "3.1.4", 315 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", 316 | "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", 317 | "requires": { 318 | "base64url": "2.0.0", 319 | "jwa": "1.1.5", 320 | "safe-buffer": "5.1.1" 321 | } 322 | }, 323 | "kareem": { 324 | "version": "1.5.0", 325 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", 326 | "integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg=" 327 | }, 328 | "lodash": { 329 | "version": "4.17.5", 330 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", 331 | "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" 332 | }, 333 | "lodash.get": { 334 | "version": "4.4.2", 335 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 336 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" 337 | }, 338 | "lodash.once": { 339 | "version": "4.1.1", 340 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 341 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 342 | }, 343 | "media-typer": { 344 | "version": "0.3.0", 345 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 346 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 347 | }, 348 | "merge-descriptors": { 349 | "version": "1.0.1", 350 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 351 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 352 | }, 353 | "methods": { 354 | "version": "1.1.2", 355 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 356 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 357 | }, 358 | "mime": { 359 | "version": "1.4.1", 360 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 361 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 362 | }, 363 | "mime-db": { 364 | "version": "1.33.0", 365 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 366 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 367 | }, 368 | "mime-types": { 369 | "version": "2.1.18", 370 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 371 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 372 | "requires": { 373 | "mime-db": "1.33.0" 374 | } 375 | }, 376 | "moment": { 377 | "version": "2.22.0", 378 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.0.tgz", 379 | "integrity": "sha512-1muXCh8jb1N/gHRbn9VDUBr0GYb8A/aVcHlII9QSB68a50spqEVLIGN6KVmCOnSvJrUhC0edGgKU5ofnGXdYdg==" 380 | }, 381 | "mongodb": { 382 | "version": "2.2.34", 383 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.34.tgz", 384 | "integrity": "sha1-o09Zu+thdUrsQy3nLD/iFSakTBo=", 385 | "requires": { 386 | "es6-promise": "3.2.1", 387 | "mongodb-core": "2.1.18", 388 | "readable-stream": "2.2.7" 389 | } 390 | }, 391 | "mongodb-core": { 392 | "version": "2.1.18", 393 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.18.tgz", 394 | "integrity": "sha1-TEYTm986HwMt7ZHbSfOO7AFlkFA=", 395 | "requires": { 396 | "bson": "1.0.6", 397 | "require_optional": "1.0.1" 398 | } 399 | }, 400 | "mongoose": { 401 | "version": "4.13.12", 402 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.13.12.tgz", 403 | "integrity": "sha512-pH8NK5AYGbnPeEFFGs5ACk18vzzcy4DFT48U9kKvkfg6SI3nJZkzGfN7o1NDWjy+kP26hWyU/AMhYTfe5hSVnA==", 404 | "requires": { 405 | "async": "2.1.4", 406 | "bson": "1.0.6", 407 | "hooks-fixed": "2.0.2", 408 | "kareem": "1.5.0", 409 | "lodash.get": "4.4.2", 410 | "mongodb": "2.2.34", 411 | "mpath": "0.3.0", 412 | "mpromise": "0.5.5", 413 | "mquery": "2.3.3", 414 | "ms": "2.0.0", 415 | "muri": "1.3.0", 416 | "regexp-clone": "0.0.1", 417 | "sliced": "1.0.1" 418 | } 419 | }, 420 | "mpath": { 421 | "version": "0.3.0", 422 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz", 423 | "integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q=" 424 | }, 425 | "mpromise": { 426 | "version": "0.5.5", 427 | "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz", 428 | "integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY=" 429 | }, 430 | "mquery": { 431 | "version": "2.3.3", 432 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.3.tgz", 433 | "integrity": "sha512-NC8L14kn+qxJbbJ1gbcEMDxF0sC3sv+1cbRReXXwVvowcwY1y9KoVZFq0ebwARibsadu8lx8nWGvm3V0Pf0ZWQ==", 434 | "requires": { 435 | "bluebird": "3.5.0", 436 | "debug": "2.6.9", 437 | "regexp-clone": "0.0.1", 438 | "sliced": "0.0.5" 439 | }, 440 | "dependencies": { 441 | "sliced": { 442 | "version": "0.0.5", 443 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", 444 | "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=" 445 | } 446 | } 447 | }, 448 | "ms": { 449 | "version": "2.0.0", 450 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 451 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 452 | }, 453 | "muri": { 454 | "version": "1.3.0", 455 | "resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz", 456 | "integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" 457 | }, 458 | "negotiator": { 459 | "version": "0.6.1", 460 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 461 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 462 | }, 463 | "on-finished": { 464 | "version": "2.3.0", 465 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 466 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 467 | "requires": { 468 | "ee-first": "1.1.1" 469 | } 470 | }, 471 | "parseurl": { 472 | "version": "1.3.2", 473 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 474 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 475 | }, 476 | "path-to-regexp": { 477 | "version": "0.1.7", 478 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 479 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 480 | }, 481 | "process-nextick-args": { 482 | "version": "1.0.7", 483 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 484 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" 485 | }, 486 | "proxy-addr": { 487 | "version": "2.0.3", 488 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 489 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 490 | "requires": { 491 | "forwarded": "0.1.2", 492 | "ipaddr.js": "1.6.0" 493 | } 494 | }, 495 | "qs": { 496 | "version": "6.5.1", 497 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 498 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 499 | }, 500 | "range-parser": { 501 | "version": "1.2.0", 502 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 503 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 504 | }, 505 | "raw-body": { 506 | "version": "2.3.2", 507 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 508 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 509 | "requires": { 510 | "bytes": "3.0.0", 511 | "http-errors": "1.6.2", 512 | "iconv-lite": "0.4.19", 513 | "unpipe": "1.0.0" 514 | }, 515 | "dependencies": { 516 | "depd": { 517 | "version": "1.1.1", 518 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 519 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 520 | }, 521 | "http-errors": { 522 | "version": "1.6.2", 523 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 524 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 525 | "requires": { 526 | "depd": "1.1.1", 527 | "inherits": "2.0.3", 528 | "setprototypeof": "1.0.3", 529 | "statuses": "1.5.0" 530 | } 531 | }, 532 | "setprototypeof": { 533 | "version": "1.0.3", 534 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 535 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 536 | } 537 | } 538 | }, 539 | "readable-stream": { 540 | "version": "2.2.7", 541 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", 542 | "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", 543 | "requires": { 544 | "buffer-shims": "1.0.0", 545 | "core-util-is": "1.0.2", 546 | "inherits": "2.0.3", 547 | "isarray": "1.0.0", 548 | "process-nextick-args": "1.0.7", 549 | "string_decoder": "1.0.3", 550 | "util-deprecate": "1.0.2" 551 | } 552 | }, 553 | "regexp-clone": { 554 | "version": "0.0.1", 555 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", 556 | "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" 557 | }, 558 | "require_optional": { 559 | "version": "1.0.1", 560 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 561 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 562 | "requires": { 563 | "resolve-from": "2.0.0", 564 | "semver": "5.5.0" 565 | } 566 | }, 567 | "resolve-from": { 568 | "version": "2.0.0", 569 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 570 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 571 | }, 572 | "safe-buffer": { 573 | "version": "5.1.1", 574 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 575 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 576 | }, 577 | "semver": { 578 | "version": "5.5.0", 579 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 580 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" 581 | }, 582 | "send": { 583 | "version": "0.16.2", 584 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 585 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 586 | "requires": { 587 | "debug": "2.6.9", 588 | "depd": "1.1.2", 589 | "destroy": "1.0.4", 590 | "encodeurl": "1.0.2", 591 | "escape-html": "1.0.3", 592 | "etag": "1.8.1", 593 | "fresh": "0.5.2", 594 | "http-errors": "1.6.3", 595 | "mime": "1.4.1", 596 | "ms": "2.0.0", 597 | "on-finished": "2.3.0", 598 | "range-parser": "1.2.0", 599 | "statuses": "1.4.0" 600 | }, 601 | "dependencies": { 602 | "statuses": { 603 | "version": "1.4.0", 604 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 605 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 606 | } 607 | } 608 | }, 609 | "serve-static": { 610 | "version": "1.13.2", 611 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 612 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 613 | "requires": { 614 | "encodeurl": "1.0.2", 615 | "escape-html": "1.0.3", 616 | "parseurl": "1.3.2", 617 | "send": "0.16.2" 618 | } 619 | }, 620 | "setprototypeof": { 621 | "version": "1.1.0", 622 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 623 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 624 | }, 625 | "sliced": { 626 | "version": "1.0.1", 627 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 628 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 629 | }, 630 | "statuses": { 631 | "version": "1.5.0", 632 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 633 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 634 | }, 635 | "string_decoder": { 636 | "version": "1.0.3", 637 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", 638 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", 639 | "requires": { 640 | "safe-buffer": "5.1.1" 641 | } 642 | }, 643 | "topo": { 644 | "version": "1.1.0", 645 | "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", 646 | "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", 647 | "requires": { 648 | "hoek": "2.16.3" 649 | } 650 | }, 651 | "type-is": { 652 | "version": "1.6.16", 653 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 654 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 655 | "requires": { 656 | "media-typer": "0.3.0", 657 | "mime-types": "2.1.18" 658 | } 659 | }, 660 | "unpipe": { 661 | "version": "1.0.0", 662 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 663 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 664 | }, 665 | "util-deprecate": { 666 | "version": "1.0.2", 667 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 668 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 669 | }, 670 | "utils-merge": { 671 | "version": "1.0.1", 672 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 673 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 674 | }, 675 | "vary": { 676 | "version": "1.1.2", 677 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 678 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 679 | }, 680 | "xtend": { 681 | "version": "4.0.1", 682 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 683 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 684 | } 685 | } 686 | } 687 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-creating-restful-apis", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "dev": "nodemon server.js", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "bcryptjs": "^2.4.3", 15 | "body-parser": "^1.16.1", 16 | "express": "^4.14.1", 17 | "jsonwebtoken": "^7.4.1", 18 | "mongoose": "^4.8.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var app = require('./app'); 2 | var port = process.env.PORT || 3000; 3 | 4 | var server = app.listen(port, function() { 5 | console.log('Express server listening on port ' + port); 6 | }); -------------------------------------------------------------------------------- /user/User.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var UserSchema = new mongoose.Schema({ 3 | name: String, 4 | email: String, 5 | password: String 6 | }); 7 | mongoose.model('User', UserSchema); 8 | 9 | module.exports = mongoose.model('User'); -------------------------------------------------------------------------------- /user/UserController.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var bodyParser = require('body-parser'); 4 | 5 | var VerifyToken = require(__root + 'auth/VerifyToken'); 6 | 7 | router.use(bodyParser.urlencoded({ extended: true })); 8 | var User = require('./User'); 9 | 10 | // CREATES A NEW USER 11 | router.post('/', function (req, res) { 12 | User.create({ 13 | name : req.body.name, 14 | email : req.body.email, 15 | password : req.body.password 16 | }, 17 | function (err, user) { 18 | if (err) return res.status(500).send("There was a problem adding the information to the database."); 19 | res.status(200).send(user); 20 | }); 21 | }); 22 | 23 | // RETURNS ALL THE USERS IN THE DATABASE 24 | router.get('/', function (req, res) { 25 | User.find({}, function (err, users) { 26 | if (err) return res.status(500).send("There was a problem finding the users."); 27 | res.status(200).send(users); 28 | }); 29 | }); 30 | 31 | // GETS A SINGLE USER FROM THE DATABASE 32 | router.get('/:id', function (req, res) { 33 | User.findById(req.params.id, function (err, user) { 34 | if (err) return res.status(500).send("There was a problem finding the user."); 35 | if (!user) return res.status(404).send("No user found."); 36 | res.status(200).send(user); 37 | }); 38 | }); 39 | 40 | // DELETES A USER FROM THE DATABASE 41 | router.delete('/:id', function (req, res) { 42 | User.findByIdAndRemove(req.params.id, function (err, user) { 43 | if (err) return res.status(500).send("There was a problem deleting the user."); 44 | res.status(200).send("User: "+ user.name +" was deleted."); 45 | }); 46 | }); 47 | 48 | // UPDATES A SINGLE USER IN THE DATABASE 49 | // Added VerifyToken middleware to make sure only an authenticated user can put to this route 50 | router.put('/:id', /* VerifyToken, */ function (req, res) { 51 | User.findByIdAndUpdate(req.params.id, req.body, {new: true}, function (err, user) { 52 | if (err) return res.status(500).send("There was a problem updating the user."); 53 | res.status(200).send(user); 54 | }); 55 | }); 56 | 57 | 58 | module.exports = router; --------------------------------------------------------------------------------