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.
Top comments (0)