Building a Scalable Blog Management System with MongoDB & Express.js
Ever tried building a blog platform and ended up with messy, unmaintainable code? You're not alone. A well-structured database design is the backbone of any successful blog system.
In this guide, we’ll architect a full-fledged Blog Management System with:
✅ User roles (Admin, Writers, Editors)
✅ Permissions & Access Control
✅ Authentication (Login/Register)
✅ Blog Analytics
✅ Optimized MongoDB Schema Design
By the end, you’ll have a production-ready Express.js API with a structured MongoDB database.
📂 Database Structure (MongoDB Collections)
1. users Collection
Stores all user accounts (Admins, Writers, Editors).
{
_id: ObjectId("..."),
username: "john_doe",
email: "john@example.com",
password: "$2a$10$hashed...", // bcrypt encrypted
role: "writer", // "admin", "editor", "writer"
isActive: true,
createdAt: ISODate("2024-05-20"),
lastLogin: ISODate("2024-05-21")
}
Indexes:
email: 1(Unique)role: 1(For role-based queries)
2. roles Collection
Defines permissions for each role (Admin, Writer, Editor).
{
_id: ObjectId("..."),
name: "writer",
permissions: [
"create:post",
"edit:own_post",
"delete:own_post"
]
}
Example Roles:
| Role | Permissions |
|---|---|
| Admin | * (All permissions) |
| Editor | edit:any_post, publish:post |
| Writer | create:post, edit:own_post |
3. posts Collection
Stores blog posts with metadata.
{
_id: ObjectId("..."),
title: "MongoDB Best Practices",
slug: "mongodb-best-practices", // SEO-friendly URL
content: "...",
author: ObjectId("..."), // Ref to `users._id`
status: "published", // "draft", "archived"
tags: ["mongodb", "database"],
createdAt: ISODate("2024-05-20"),
updatedAt: ISODate("2024-05-21"),
views: 1250 // For analytics
}
Indexes:
slug: 1(Unique)author: 1tags: 1(For tag filtering)
4. analytics Collection
Tracks post views, user engagement.
{
_id: ObjectId("..."),
postId: ObjectId("..."), // Ref to `posts._id`
date: ISODate("2024-05-21"),
views: 50,
reads: 30 // Users who read full post
}
Indexes:
postId: 1, date: 1(For time-based analytics)
🔐 Authentication System (Login/Register)
1. User Registration
Endpoint: POST /api/auth/register
// Input Validation (Joi)
{
username: Joi.string().required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
role: Joi.string().valid("writer", "editor") // Admins created manually
}
// Password hashing (bcrypt)
const hashedPassword = await bcrypt.hash(password, 10);
const user = await User.create({ username, email, password: hashedPassword, role });
2. User Login (JWT)
Endpoint: POST /api/auth/login
// Check user exists
const user = await User.findOne({ email });
if (!user) throw new Error("Invalid credentials");
// Verify password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) throw new Error("Invalid credentials");
// Generate JWT
const token = jwt.sign(
{ userId: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: "1d" }
);
return { token, user: { id: user._id, role: user.role } };
🛡 Role-Based Access Control (Middleware)
// middleware/auth.js
const checkPermission = (requiredPermission) => (req, res, next) => {
const userRole = req.user.role;
const role = await Role.findOne({ name: userRole });
if (!role.permissions.includes(requiredPermission)) {
return res.status(403).json({ error: "Access denied" });
}
next();
};
Usage in Routes:
// Only editors/admins can publish posts
router.post(
"/posts/:id/publish",
authMiddleware,
checkPermission("publish:post"),
publishPost
);
📊 Analytics Endpoints
1. Get Post Views Over Time
Endpoint: GET /api/analytics/posts/:postId
const analytics = await Analytics.aggregate([
{ $match: { postId: mongoose.Types.ObjectId(postId) } },
{ $group: { _id: "$date", totalViews: { $sum: "$views" } } },
{ $sort: { _id: 1 } } // Sort by date
]);
2. Top Trending Posts
const trending = await Post.aggregate([
{ $sort: { views: -1 } },
{ $limit: 10 },
{ $project: { title: 1, views: 1 } }
]);
🚀 Express.js API Structure
blog-api/
├── config/
│ ├── db.js # MongoDB connection
│ └── jwt.js # JWT settings
├── controllers/
│ ├── authController.js
│ ├── postController.js
│ └── analyticsController.js
├── models/
│ ├── User.js
│ ├── Post.js
│ └── Analytics.js
├── routes/
│ ├── authRoutes.js
│ ├── postRoutes.js
│ └── analyticsRoutes.js
├── middleware/
│ ├── auth.js # JWT verification
│ └── permissions.js # Role checks
└── app.js # Express setup
💡 Pro Tips for Optimization
- Use Mongoose Hooks for auto-updating
updatedAt:postSchema.pre('save', function(next) { this.updatedAt = new Date(); next(); }); - Cache Popular Posts (Redis) to reduce DB load.
- Text Indexing for search:
postSchema.index({ title: "text", content: "text" });
✅ Final Thoughts
You now have a scalable blog management system with:
✔ Secure user authentication
✔ Granular role-based permissions
✔ Detailed analytics tracking
✔ Optimized MongoDB schemas
What’s next?
👉 Try adding commenting functionality
👉 Implement SSE (Server-Sent Events) for real-time stats
Got questions? Ask below! 🚀
Projects to explore next
Source-code projects from the KritiMyantra catalogue. Check each project's details before buying.
Flask Authentication System (Free Version) - Kritim Yantra
🛠 Features✔ Google & GitHub Authentication✔ User Registration & Login✔ Profile/Dashboard Access✔ Secure & Mi...
Flask Blog CMS with Admin Dashboard — Python Source Code
A Flask-based blogging website with a built-in admin dashboard. Manage articles, images, categories, tags and featured p...
Laravel 12 + ReactJS Starter Kit: Add Multi-Language Support (Arabic, English, Spanish) with Blog CRUD
🚀 Installation & Setup Download & Extract Get the ZIP from kritimyantra.com and extract it. Includes preconfigu...
Comments
No comments yet. Be the first to comment.
Sign in to join the discussion.