├── .gitignore ├── README.md ├── app.js ├── controllers ├── deck.js └── user.js ├── helpers └── routerHelpers.js ├── models ├── Deck.js └── User.js ├── package.json └── routes ├── deck.js └── user.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodejsApiAuthentication 2 | Repo cho khóa học xây dựng Node.js API cho người mới bắt đầu được chia sẻ trên kênh RHP Team Youtube. 3 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser') 2 | const express = require('express') 3 | const logger = require('morgan') 4 | const mongoClient = require('mongoose') 5 | 6 | // setup connect mongodb by mongoose 7 | mongoClient.connect('mongodb://localhost/nodejsapistarter', { 8 | useNewUrlParser: true, 9 | useUnifiedTopology: true 10 | }) 11 | .then(() => console.log('✅ Connected database from mongodb.')) 12 | .catch((error) => console.error(`❌ Connect database is failed with error which is ${error}`)) 13 | 14 | const app = express() 15 | 16 | const deckRoute = require('./routes/deck') 17 | const userRoute = require('./routes/user') 18 | 19 | // Middlewares 20 | app.use(logger('dev')) 21 | app.use(bodyParser.json()) 22 | 23 | // Routes 24 | app.use('/decks', deckRoute) 25 | app.use('/users', userRoute) 26 | 27 | // Routes 28 | app.get('/', (req, res, next) => { 29 | return res.status(200).json({ 30 | message: 'Server is OK!' 31 | }) 32 | }) 33 | 34 | // Catch 404 Errors and forward them to error handler 35 | app.use((req, res, next) => { 36 | const err = new Error('Not Found') 37 | err.status = 404 38 | next(err) 39 | }) 40 | 41 | // Error handler function 42 | app.use((err, req, res, next) => { 43 | const error = app.get('env') === 'development' ? err : {} 44 | const status = err.status || 500 45 | 46 | // response to client 47 | return res.status(status).json({ 48 | error: { 49 | message: error.message 50 | } 51 | }) 52 | }) 53 | 54 | // Start the server 55 | const port = app.get('port') || 3000 56 | app.listen(port, () => console.log(`Server is listening on port ${port}`)) -------------------------------------------------------------------------------- /controllers/deck.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We can interact with mongoose in three diffirent ways: 3 | * [v] Callback 4 | * [v] Promises 5 | * [v] Async/await (Promises) 6 | */ 7 | 8 | const Deck = require('../models/Deck') 9 | const User = require('../models/User') 10 | 11 | const deleteDeck = async (req, res, next) => { 12 | const { deckID } = req.value.params 13 | 14 | // Get a deck 15 | const deck = await Deck.findById(deckID) 16 | const ownerID = deck.owner 17 | 18 | // Get a owner 19 | const owner = await User.findById(ownerID) 20 | 21 | // Remove the deck 22 | await deck.remove() 23 | 24 | // Remove deck from owner's decks list 25 | owner.decks.pull(deck) 26 | await owner.save() 27 | 28 | return res.status(200).json({ success: true }) 29 | } 30 | 31 | const getDeck = async (req, res, next) => { 32 | const deck = await Deck.findById(req.value.params.deckID) 33 | 34 | return res.status(200).json({deck}) 35 | } 36 | 37 | const index = async (req, res, next) => { 38 | const decks = await Deck.find({}) 39 | 40 | return res.status(200).json({decks}) 41 | } 42 | 43 | const newDeck = async (req, res, next) => { 44 | // Find owner 45 | const owner = await User.findById(req.value.body.owner) 46 | 47 | // Create a new deck 48 | const deck = req.value.body 49 | delete deck.owner 50 | 51 | deck.owner = owner._id 52 | const newDeck = new Deck(deck) 53 | await newDeck.save() 54 | 55 | // Add newly created deck to the actual decks 56 | owner.decks.push(newDeck._id) 57 | await owner.save() 58 | 59 | return res.status(201).json({deck: newDeck}) 60 | } 61 | 62 | const replaceDeck = async (req, res, next) => { 63 | const { deckID } = req.value.params 64 | const newDeck = req.value.body 65 | const result = await Deck.findByIdAndUpdate(deckID, newDeck) 66 | // Check if put user, remove deck in user's model 67 | return res.status(200).json({ success: true }) 68 | } 69 | 70 | const updateDeck = async (req, res, next) => { 71 | const { deckID } = req.value.params 72 | const newDeck = req.value.body 73 | const result = await Deck.findByIdAndUpdate(deckID, newDeck) 74 | // Check if put user, remove deck in user's model 75 | return res.status(200).json({ success: true }) 76 | } 77 | 78 | module.exports = { 79 | deleteDeck, 80 | getDeck, 81 | index, 82 | newDeck, 83 | replaceDeck, 84 | updateDeck 85 | } -------------------------------------------------------------------------------- /controllers/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We can interact with mongoose in three diffirent ways: 3 | * [v] Callback 4 | * [v] Promises 5 | * [v] Async/await (Promises) 6 | */ 7 | 8 | const Deck = require('../models/Deck') 9 | const User = require('../models/User') 10 | 11 | const getUser = async (req, res, next) => { 12 | const { userID } = req.value.params 13 | 14 | const user = await User.findById(userID) 15 | 16 | return res.status(200).json({user}) 17 | } 18 | 19 | const getUserDecks = async (req, res, next) => { 20 | const { userID } = req.value.params 21 | 22 | // Get user 23 | const user = await User.findById(userID).populate('decks') 24 | 25 | return res.status(200).json({decks: user.decks}) 26 | } 27 | 28 | const index = async (req, res, next) => { 29 | const users = await User.find({}) 30 | 31 | return res.status(200).json({users}) 32 | } 33 | 34 | const newUser = async (req, res, next) => { 35 | const newUser = new User(req.value.body) 36 | 37 | await newUser.save() 38 | 39 | return res.status(201).json({user: newUser}) 40 | } 41 | 42 | const newUserDeck = async (req, res, next) => { 43 | const { userID } = req.value.params 44 | 45 | // Create a new deck 46 | const newDeck = new Deck(req.value.body) 47 | 48 | // Get user 49 | const user = await User.findById(userID) 50 | 51 | // Assign user as a deck's owner 52 | newDeck.owner = user 53 | 54 | // Save the deck 55 | await newDeck.save() 56 | 57 | // Add deck to user's decks array 'decks' 58 | user.decks.push(newDeck._id) 59 | 60 | // Save the user 61 | await user.save() 62 | 63 | res.status(201).json({deck: newDeck}) 64 | } 65 | 66 | const replaceUser = async (req, res, next) => { 67 | // enforce new user to old user 68 | const { userID } = req.value.params 69 | 70 | const newUser = req.value.body 71 | 72 | const result = await User.findByIdAndUpdate(userID, newUser) 73 | 74 | return res.status(200).json({success: true}) 75 | } 76 | 77 | const updateUser = async (req, res, next) => { 78 | // number of fields 79 | const { userID } = req.value.params 80 | 81 | const newUser = req.value.body 82 | 83 | const result = await User.findByIdAndUpdate(userID, newUser) 84 | 85 | return res.status(200).json({success: true}) 86 | } 87 | 88 | module.exports = { 89 | getUser, 90 | getUserDecks, 91 | index, 92 | newUser, 93 | newUserDeck, 94 | replaceUser, 95 | updateUser 96 | } -------------------------------------------------------------------------------- /helpers/routerHelpers.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi') 2 | 3 | const validateBody = (schema) => { 4 | return (req, res, next) => { 5 | const validatorResult = schema.validate(req.body) 6 | 7 | if (validatorResult.error) { 8 | return res.status(400).json(validatorResult.error) 9 | } else { 10 | if (!req.value) req.value = {} 11 | if (!req.value['params']) req.value.params = {} 12 | 13 | req.value.body = validatorResult.value 14 | next() 15 | } 16 | } 17 | } 18 | 19 | const validateParam = (schema, name) => { 20 | return (req, res, next) => { 21 | const validatorResult = schema.validate({param: req.params[name]}) 22 | 23 | if (validatorResult.error) { 24 | return res.status(400).json(validatorResult.error) 25 | } else { 26 | if (!req.value) req.value = {} 27 | if (!req.value['params']) req.value.params = {} 28 | 29 | req.value.params[name] = req.params[name] 30 | next() 31 | } 32 | } 33 | } 34 | 35 | const schemas = { 36 | deckSchema: Joi.object().keys({ 37 | name: Joi.string().min(6).required(), 38 | description: Joi.string().min(10).required() 39 | }), 40 | 41 | deckOptionalSchema: Joi.object().keys({ 42 | name: Joi.string().min(6), 43 | description: Joi.string().min(10), 44 | owner: Joi.string().regex(/^[0-9a-fA-F]{24}$/) 45 | }), 46 | 47 | idSchema: Joi.object().keys({ 48 | param: Joi.string().regex(/^[0-9a-fA-F]{24}$/).required() 49 | }), 50 | 51 | newDeckSchema: Joi.object().keys({ 52 | name: Joi.string().min(6).required(), 53 | description: Joi.string().min(10).required(), 54 | owner: Joi.string().regex(/^[0-9a-fA-F]{24}$/).required() 55 | }), 56 | 57 | userSchema: Joi.object().keys({ 58 | firstName: Joi.string().min(2).required(), 59 | lastName: Joi.string().min(2).required(), 60 | email: Joi.string().email().required() 61 | }), 62 | 63 | userOptionalSchema: Joi.object().keys({ 64 | firstName: Joi.string().min(2), 65 | lastName: Joi.string().min(2), 66 | email: Joi.string().email() 67 | }) 68 | } 69 | 70 | module.exports = { 71 | validateBody, 72 | validateParam, 73 | schemas 74 | } -------------------------------------------------------------------------------- /models/Deck.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const Schema = mongoose.Schema 3 | 4 | const DeckSchema = new Schema({ 5 | name: { 6 | type: String 7 | }, 8 | description: { 9 | type: String 10 | }, 11 | total: { 12 | type: Number, 13 | default: 0 14 | }, 15 | owner: { 16 | type: Schema.Types.ObjectId, 17 | ref: 'User' 18 | } 19 | }) 20 | 21 | const Deck = mongoose.model('Deck', DeckSchema) 22 | module.exports = Deck -------------------------------------------------------------------------------- /models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | const Schema = mongoose.Schema 3 | 4 | const UserSchema = new Schema({ 5 | firstName: { 6 | type: String 7 | }, 8 | lastName: { 9 | type: String 10 | }, 11 | email: { 12 | type: String 13 | }, 14 | decks: [{ 15 | type: Schema.Types.ObjectId, 16 | ref: 'Deck' 17 | }] 18 | }) 19 | 20 | const User = mongoose.model('User', UserSchema) 21 | module.exports = User -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NodejsApiAuthentication", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "node app.js", 8 | "dev": "nodemon app.js" 9 | }, 10 | "dependencies": { 11 | "@hapi/joi": "^17.1.1", 12 | "body-parser": "^1.19.0", 13 | "express": "^4.17.1", 14 | "express-promise-router": "^3.0.3", 15 | "mongoose": "^5.9.9" 16 | }, 17 | "devDependencies": { 18 | "morgan": "^1.10.0", 19 | "nodemon": "^2.0.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /routes/deck.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | // const router = express.Router() 3 | const router = require('express-promise-router')() 4 | 5 | const DeckController = require('../controllers/deck') 6 | 7 | const { validateBody, validateParam, schemas } = require('../helpers/routerHelpers') 8 | 9 | router.route('/') 10 | .get(DeckController.index) 11 | .post(validateBody(schemas.newDeckSchema), DeckController.newDeck) 12 | 13 | router.route('/:deckID') 14 | .get(validateParam(schemas.idSchema, 'deckID'), DeckController.getDeck) 15 | .put(validateParam(schemas.idSchema, 'deckID'), validateBody(schemas.newDeckSchema), DeckController.replaceDeck) 16 | .patch(validateParam(schemas.idSchema, 'deckID'), validateBody(schemas.deckOptionalSchema), DeckController.updateDeck) 17 | .delete(validateParam(schemas.idSchema, 'deckID'), DeckController.deleteDeck) 18 | 19 | module.exports = router -------------------------------------------------------------------------------- /routes/user.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | // const router = express.Router() 3 | const router = require('express-promise-router')() 4 | 5 | const UserController = require('../controllers/user') 6 | 7 | const { validateBody, validateParam, schemas } = require('../helpers/routerHelpers') 8 | 9 | router.route('/') 10 | .get(UserController.index) 11 | .post(validateBody(schemas.userSchema), UserController.newUser) 12 | 13 | router.route('/:userID') 14 | .get(validateParam(schemas.idSchema, 'userID'), UserController.getUser) 15 | .put(validateParam(schemas.idSchema, 'userID'), validateBody(schemas.userSchema), UserController.replaceUser) 16 | .patch(validateParam(schemas.idSchema, 'userID'), validateBody(schemas.userOptionalSchema), UserController.updateUser) 17 | 18 | router.route('/:userID/decks') 19 | .get(validateParam(schemas.idSchema, 'userID'), UserController.getUserDecks) 20 | .post(validateParam(schemas.idSchema, 'userID'), validateBody(schemas.deckSchema), UserController.newUserDeck) 21 | 22 | module.exports = router --------------------------------------------------------------------------------