Backend For MERN app
Swislok-Dev
Posted on May 22, 2022
Configure
- Mongo Atlas database
- Error Handler
- User Model
- User Controller
- User Authorization Middleware
- User Routes
- Express Server
Configure database for Mongo Atlas
To get started run npm install
for the following dependencies:
dotenv
express
express-async-handler
mongoose
jsonwebtoken
brcyptjs
It will be helpful to also include npm install nodemon -D
to be required for automatic restarts of the node server when making file changes during development.
Create a .env
file as well to contain:
- NODE_ENV = development
- MONGO_URI = url to mongo database
- PORT = 5001
- JWT_SECRET = secretpeaches
./backend/config/db.js
This file will house how to connect to the Mongo database that once created via mongodb.com, allows for a remote database connection without the need to store locally.
const mongoose = require('mongoose')
const connectDB = async () => {
try {
const conn - await mongoose.connect(process.env.MONGO_URI)
// Include confirmation of connecting to Mongo
console.log(`MongoDB connected ${conn.connection.host}`)
} catch (error) {
console.log('MongoDB not connected', error)
process.exit(1)
}
}
// Export the module for use in `index.js`
module.exports = connectDB
Configure Error Handler
Having a custom error handler will help for debugging feedback to trace where problems may have occurred.
./backend/middleware/errorMiddleware.js
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode ? res.statusCode : 500
// Call error code retrieved from the response
res.status(statusCode)
res.json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? null : err.stack,
})
}
module.exports = { errorHandler }
Configure User Model
The user model will contain name
, email
, and password
. The password will be hashed and salted in another file coming up.
./backend/models/userModel.js
const mongoose = require('mongoose')
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: [true , 'Please provide a name'],
},
email: {
type: String,
required: [true, 'Please provide an email'],
unique: true,
},
password: {
type: String,
required: [true, 'Please provide a password'],
},
},
{
timestamps: true,
}
)
// Export the name of the model ('User') and the module itself (userSchema)
module.exports = mongoose.model('User', userSchema)
Configure User Controller
The controller will hold the code for all user actions.
./backend/controllers/userController.js
const jwt = require('jswonwebtoken')
const bcrypt = require('bcryptjs')
const asyncHandler = require('express-async-handler')
const User = require('../models/userModel')
const registerUser = asyncHandler(async (req, res) => {
// Destructure attributes from request
const { name, email, password } = req.body
// Check for missing information on the form
if (!name || !email || !password) {
res.status(400)
throw new Error('Please fill in all required fields')
}
const userExists = await User.findOne({ email })
if (userExists) {
res.status(400)
throw new Error('User already exists')
// Optional to redirect to login page
}
// Hash password
const salt = await bcrypt.genSalt(24)
// Take in `password` and use `salt` for hashing algorithm
const hashedPassword = await bcrypt.hash(password, salt)
const user = await User.create({
name, email, password: hashedPassword
})
if (user) {
res.status(201).json({
_id: user.id,
name: user.name,
email: user.email,
token: generateToken(user._id),
})
} else {
res.status(400)
throw new Error('Invalid user data')
}
})
const loginUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user && (await bcrypt.compare(password, user.password))) {
res.json({
_id: user.id,
name: user.name,
email: user.email,
token: generateToken(user._id),
})
} else {
res.status(400)
throw new Error('Invalid credentials')
}
})
const generateToken = (id) => {
return jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: '30d', // Determines when this token will expire and user will need to login again
})
}
module.exports = {
registerUser,
loginUser
}
Configure User Authorization Middleware
Authorization will be required by the user to access any data that is owned by the user such as user details, created assets, or settings.
./backend/middleware/authMiddleware.js
const jwt = require('jsonwebtoken')
const asyncHandler = require('express-async-handler')
const User = require('../models/userModel')
const protect = asyncHandler(async (req, res, next) => {
let token
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
try {
token = req.headers.authorization.split(' ')[1]
const decodedToken = jwt.verify(token, process.env.JWT_SECRET)
req.user = await User.findById(decodedToken.id).select('-password')
next()
} catch (error) {
console.log(error)
res.status(401)
throw new Error('Not authorized')
}
}
if (!token) {
res.status(401)
throw new Error('Not authorized, no token')
}
})
module.exports = { protect }
Configure User Routes
Routes that will be used by express
to act upon actions by and for the user while utilizing the middleware to maintain authorization.
./backend/routes/userRoutes.js
const express = require('express')
const router = express.Router()
const { registerUser, loginUser } = require('../controllers/userController')
const { protect } = require('../middleware/authMiddleware')
// POST route api/users/
router.post('/', registerUser)
// POST route api/users/login
router.post('/login', loginUser)
module.exports = router
Configure Express Server
This is the launching point for express
to send information out and allow access to the rest of the backend files with custom error handling.
./backend/index.js
const express = require('express')
const dotenv = require('dotenv').config()
const connectDB = require('./config/db')
const { errorhandler } = require('./middleware/errorMiddleware')
const port = process.env.PORT || 5001
// Connect to Mongo Atlas database
connectDB()
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: false })
// Use user routes
app.use('/api/users', require('./routes/userRoutes'))
app.use(errorhandler)
// Port to be used for the server to run on
app.listen(port, () => console.log(`Server running on port ${port}`))
Conclusion
This was a basic boilerplate for setting up an express server for users to be customized as needed for the next use case.
Posted on May 22, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.