Creating a Blogging Website Using Node.js, JWT Tokens, Express, MongoDB, and Hashing
In this tutorial, I will walk through the process of creating a blogging website using Node.js, Express, MongoDB for database storage, JWT tokens for user authentication, and hashing for secure password storage. We'll cover essential backend and frontend aspects, deployment considerations, and more.
Project Setup
Dependencies Installation
Make sure you have Node.js and npm installed. Initialize your project and install necessary dependencies:
npm init -y
npm install express mongoose ejs cookie-parser dotenv bcrypt jsonwebtoken multer
GitHub Repository
You can find the complete code for this project on GitHub: Blogging Website GitHub Repository
Backend Development
Connecting to MongoDB
Ensure MongoDB is installed and running. Connect your Node.js application to MongoDB using Mongoose:
// app.js
const express = require('express');
const mongoose = require('mongoose');
const cookieParser = require('cookie-parser');
const dotenv = require('dotenv');
const userRoute = require('./routes/user');
const blogRoute = require('./routes/blog');
const app = express();
const PORT = process.env.PORT || 8000;
dotenv.config();
mongoose.connect(process.env.MONGO_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('MongoDB Connected'))
.catch((err) => console.error('MongoDB connection error:', err));
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static('public'));
// Routes
app.use('/user', userRoute);
app.use('/blog', blogRoute);
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
User Authentication with JWT Tokens
Implement user signup, signin, and authentication using JWT tokens:
// routes/user.js
const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('../models/User');
// User signup
router.post('/signup', async (req, res) => {
const { fullName, email, password } = req.body;
try {
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create new user
const newUser = await User.create({
fullName,
email,
password: hashedPassword,
});
res.status(201).json({ message: 'User created successfully', user: newUser });
} catch (error) {
console.error('Error in user signup:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
// User signin
router.post('/signin', async (req, res) => {
const { email, password } = req.body;
try {
// Find user by email
const user = await User.findOne({ email });
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
// Compare passwords
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).json({ error: 'Incorrect password' });
}
// Generate JWT token
const token = jwt.sign({ userId: user._id, email: user.email }, process.env.JWT_SECRET, {
expiresIn: '1h',
});
res.cookie('token', token, { httpOnly: true }).json({ message: 'Signin successful', token });
} catch (error) {
console.error('Error in user signin:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
module.exports = router;
Frontend Integration
Templating with EJS
Create frontend views using EJS templates for user signup, signin, and blog posts:
<!-- views/signup.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
<%- include('./partials/head') %>
<title>Signup Page</title>
</head>
<body>
<%- include('./partials/nav') %>
<div class="container mt-4">
<form action="/user/signup" method="post">
<div class="mb-3">
<label for="fullName" class="form-label">Full Name</label>
<input type="text" name="fullName" class="form-control" id="fullName" aria-describedby="fullName">
</div>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input name="password" type="password" class="form-control" id="exampleInputPassword1">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<%- include('./partials/scripts') %>
</body>
</html>
Security Measures
Hashing Passwords
Securely hash user passwords using bcrypt before saving them in the database:
// models/User.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt');
const userSchema = new Schema({
fullName: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
// Hash password before saving
userSchema.pre('save', async function (next) {
const user = this;
if (!user.isModified('password')) return next();
try {
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
return next();
} catch (error) {
return next(error);
}
});
module.exports = mongoose.model('User', userSchema);
Deployment and Testing
Deployment on Heroku
Deploy your Node.js application on Heroku, including configuring environment variables and deploying MongoDB databases.
Testing and Debugging
Test backend routes and frontend interactions, and debug common issues such as database connection errors and token authentication problems.
This comprehensive guide covers essential aspects of building a blogging website with Node.js and Express, focusing on backend development, frontend integration using EJS, security measures like password hashing, and deployment considerations. For the complete project code and detailed implementation, visit the GitHub repository: Blogging Website GitHub Repository
Subscribe to my newsletter
Read articles from Rahul Boney directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Rahul Boney
Rahul Boney
Hey, I'm Rahul Boney, really into Computer Science and Engineering. I love working on backend development, exploring machine learning, and diving into AI. I am always excited about learning and building new things.