Node.js New Project Boilerplate
https://github.com/enesccinar/nodejs-boiler
git clone <project-addr>
npm init -y
npm i nodemon
npm i express
npm i bcryptjs
npm i jsonwebtoken
npm i mongoose
npm i cors
npm i body-parser
npm i lodash
npm i validator
Git Ignore
Add .gitignore file to the project root folder.
node_modules
Project Structure
- SRC
- app.js
- models
- user.js
- routers
- user.js
- db
- mongoose.js
- middleware
- auth.js
- utils
Main JS
I add app.js to the main directory. I have used this file to manage the main flow.
const cors = require('cors')
const express = require('express')
const bodyParser = require('body-parser')
require('./db/mongoose')
const userRouter = require('./routers/user')
const app = express()
const port = process.env.PORT || 3000
app.use(express.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cors())
app.use(userRouter)
app.listen(port, () => {
console.log('server is up on port: ' + port)
})
Package.json
"scripts": {
"dev": "nodemon src/app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
Middleware – Auth
const jwt = require('jsonwebtoken')
const User = require('../models/user')
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '')
const decoded = jwt.verify(token, 'louie-inspector')
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })
if (!user)
throw new Error()
req.token = token
req.user = user
next()
} catch (e) {
console.warn(e.message)
req.status(401).send({ error: 'Please authenticate' })
}
}
module.exports = auth
DB – Mongoose
const mongoose = require('mongoose')
require('../utils/config')
mongoose.connect(global.gConfig.database, {
useNewUrlParser: true,
useCreateIndex: true
})
User Model
const mongoose = require('mongoose')
const validator = require('validator')
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const userSchema = new mongoose.Schema({
name: {
type: String,
require: true
},
email: {
type: String,
unique: true,
require: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
password: {
type: String,
required: true,
minlength: 7,
trim: true
},
tokens: [{
token: {
type: String,
required: true
}
}]
})
userSchema.methods.toJSON = function () {
const user = this
const userObject = user.toObject()
delete userObject.password
delete userObject.tokens
return userObject
}
userSchema.methods.generateAuthToken = async function () {
const user = this
const token = jwt.sign({ _id: user._id.toString() }, 'louie-inspector')
user.tokens = user.tokens.concat({ token })
await user.save()
return token
}
userSchema.statics.findByCredentials = async (email, password) => {
const user = await user.findOne({ email })
if (!user)
throw new Error('Unable to login!')
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch)
throw new Error('Unable to login')
return user
}
userSchema.pre('save', async function (next) {
const user = this
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 8)
}
next()
})
const User = mongoose.model('User', userSchema)
module.exports = User
User Router
const express = require('express')
const User = require('../models/user')
const auth = require('../middleware/auth')
const router = new express.Router()
router.post('/users', async (req, res) => {
const user = new User(req.body)
try {
await user.save()
const token = await user.generateAuthToken()
res.status(201).send({ user, token })
} catch (e) {
res.status(400).send(e)
}
})
router.post('/users/login', async (req, res) => {
try {
const user = await User.findByCredentials(req.body.email, req.body.password)
const token = await user.generateAuthToken()
res.send({ user, token })
} catch (e) {
res.status(400).send(e)
}
})
router.post('/users/logout', auth, async (req, res) => {
try {
req.user.tokens = req.user.tokens.filter((token) => token.token !== req.token)
await req.user.save()
res.send()
} catch (e) {
res.status(500).send()
}
})
router.post('/users/logoutall', auth, async (req, res) => {
try {
req.user.tokens = []
await res.user.save()
res.send()
} catch (e) {
res.status(500).send()
}
})
router.get('/users/me', auth, async (req, res) => {
res.send(req.user)
})
router.patch('/users/me', auth, async (req, res) => {
const updates = Object.jeys(req.body)
const allowedUpdated = ['name', 'email', 'password']
const isValidOperation = updates.every((update) => allowedUpdated.includes(update))
if (!isValidOperation)
return res.status(400).send({ error: 'Invalid updates!' })
try {
updates.forEach((update) => req.user[update] = req.body[update])
await req.user.save()
res.send(req.user)
} catch (e) {
res.status(400).send(e)
}
})
router.delete('/users/me', auth, async (req, res) => {
try {
await req.user.remove()
res.send(req.user)
} catch (e) {
res.status(500).send()
}
})
module.exports = router