Capstone Architecture, Planning and Feature Scope

The capstone is where every concept from the previous 27 chapters comes together into a single, deployable application. Rather than introducing new technology, the capstone exercises your ability to make architectural decisions, reason about data relationships, plan an API surface, and structure a full-stack project before writing a single line of code. In this lesson you will define the complete feature scope for the MERN Blog/CMS, design the MongoDB schema for every collection, map every API endpoint, and set up the monorepo project structure โ€” producing a blueprint that the next four lessons will build from.

Capstone Feature Scope

Feature Area Included Features
Authentication Register, login, logout, email verification, password reset, refresh token, profile update
Posts Create, read, update, delete, publish/draft toggle, featured flag, tag filtering, full-text search, pagination
Comments Create, delete, list by post, live updates via Socket.io, typing indicator
Tags Auto-created on post save, tag cloud on home page, filtered post list by tag
Media Avatar upload (user profile), cover image upload (post), Cloudinary CDN storage
Email Welcome + verification, password reset, password changed alert
Admin Admin dashboard โ€” all users, all posts, delete any post, promote/demote role
Analytics Post view count (increment on GET), like count, comment count
Note: Scope creep is the most common cause of unfinished capstone projects. Define a clear MVP (Minimum Viable Product) โ€” the smallest set of features that makes the application genuinely usable โ€” and build that first. For the MERN Blog, the MVP is: register, login, create post, view posts, comment on a post, and deploy to production. Everything else (tags, search, admin panel, real-time) is a second phase that adds value to an already working application.
Tip: Build vertically, not horizontally. “Vertical” means one complete feature at a time from database to API to React component โ€” registration working end-to-end before you start on post creation. “Horizontal” means building all the Express routes first, then all the React components โ€” you cannot test anything end-to-end until everything is done. Vertical slices let you demo a working feature after each work session, which is motivating and catches integration bugs early.
Warning: Do not copy-paste large blocks of code from previous chapters without understanding them. The capstone’s value is in making your own design decisions and writing the code yourself โ€” that is what solidifies the learning. Use the previous chapters as reference, not as a copy source. When you get stuck, re-read the relevant chapter; when you are building, write the code fresh from your understanding of what each piece does.

MongoDB Schema Design

// โ”€โ”€ User โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
{
  _id, name, email, password (hashed), role ('user'|'editor'|'admin'),
  avatar (Cloudinary URL), bio, isEmailVerified,
  emailVerifyToken (select:false), emailVerifyExpires (select:false),
  passwordResetToken (select:false), passwordResetExpires (select:false),
  passwordChangedAt (select:false),
  createdAt, updatedAt
}

// โ”€โ”€ Post โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
{
  _id, title, slug (unique, auto-generated from title),
  body (HTML string), excerpt, coverImage (Cloudinary URL),
  author (ref: User), tags ([String]),
  published (Boolean, default:false), featured (Boolean, default:false),
  viewCount (Number, default:0), likedBy ([ref: User]),
  createdAt, updatedAt
}

// โ”€โ”€ Comment โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
{
  _id, body, post (ref: Post), author (ref: User),
  createdAt, updatedAt
}

// โ”€โ”€ Tag (optional โ€” derived from posts, or a separate collection) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
// Option A: tags stored as string arrays on Post โ€” simpler, no separate collection
// Option B: Tag collection for tag descriptions and post count caching
// Recommendation: start with Option A (strings on Post), add Tag collection if needed

API Endpoint Map

โ”€โ”€ Auth โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
POST   /api/auth/register           Register + send verification email
POST   /api/auth/login              Login โ†’ access token + refresh cookie
POST   /api/auth/logout             Clear refresh token cookie
POST   /api/auth/refresh            Issue new access token from refresh cookie
GET    /api/auth/me                 Get current user (protected)
GET    /api/auth/verify-email       Verify email with token
POST   /api/auth/forgot-password    Send password reset email
POST   /api/auth/reset-password/:t  Reset password with token

โ”€โ”€ Posts โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
GET    /api/posts                   List (published, paginated, filterable by tag/search)
POST   /api/posts                   Create (protected)
GET    /api/posts/:id               Get single post + increment viewCount
PATCH  /api/posts/:id               Update (owner or admin)
DELETE /api/posts/:id               Delete (owner or admin)
PATCH  /api/posts/:id/publish       Toggle published (owner or editor/admin)
POST   /api/posts/:id/like          Like / unlike toggle (protected)
GET    /api/posts/featured          Get featured posts (public)
GET    /api/posts/search?q=         Full-text search (public)

โ”€โ”€ Comments โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
GET    /api/posts/:postId/comments  List comments for a post
POST   /api/posts/:postId/comments  Create comment (protected) โ†’ Socket.io emit
DELETE /api/comments/:id            Delete comment (owner or admin)

โ”€โ”€ Users โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
GET    /api/users/:id               Public profile
PATCH  /api/users/:id               Update profile (own account)
POST   /api/users/avatar            Upload avatar (protected, Multer+Cloudinary)
GET    /api/users/:id/posts         Posts by user (public)

โ”€โ”€ Admin โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
GET    /api/admin/users             List all users (admin only)
PATCH  /api/admin/users/:id/role    Change role (admin only)
DELETE /api/admin/users/:id         Delete user (admin only)
GET    /api/admin/stats             Site stats โ€” post/user/comment counts

โ”€โ”€ Upload โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
POST   /api/posts/:id/cover         Upload cover image (owner, Cloudinary)

โ”€โ”€ Health โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
GET    /api/health                  Health check

Monorepo Project Structure

mern-blog/
โ”œโ”€โ”€ server/                     Express API
โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ config/             db.js, cloudinary.js, multer.js, socket.js
โ”‚   โ”‚   โ”œโ”€โ”€ controllers/        authController.js, postController.js, ...
โ”‚   โ”‚   โ”œโ”€โ”€ middleware/         auth.js, authorize.js, errorHandler.js, rateLimiter.js
โ”‚   โ”‚   โ”œโ”€โ”€ models/             User.js, Post.js, Comment.js
โ”‚   โ”‚   โ”œโ”€โ”€ routes/             auth.js, posts.js, comments.js, users.js, admin.js
โ”‚   โ”‚   โ””โ”€โ”€ utils/              asyncHandler.js, AppError.js, jwt.js, sendEmail.js, ...
โ”‚   โ”œโ”€โ”€ index.js
โ”‚   โ”œโ”€โ”€ package.json
โ”‚   โ””โ”€โ”€ .env                    (gitignored)
โ”‚
โ”œโ”€โ”€ client/                     React + Vite SPA
โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ components/         layout/, posts/, ui/, auth/
โ”‚   โ”‚   โ”œโ”€โ”€ context/            AuthContext.jsx, NotificationContext.jsx
โ”‚   โ”‚   โ”œโ”€โ”€ hooks/              useSocket.js, useFetch.js, useDebounce.js
โ”‚   โ”‚   โ”œโ”€โ”€ pages/              HomePage, PostDetailPage, LoginPage, ...
โ”‚   โ”‚   โ”œโ”€โ”€ services/           api.js, postService.js, authService.js
โ”‚   โ”‚   โ””โ”€โ”€ utils/              validators.js, formatters.js
โ”‚   โ”œโ”€โ”€ public/_redirects       /* /index.html 200
โ”‚   โ”œโ”€โ”€ package.json
โ”‚   โ””โ”€โ”€ vite.config.js
โ”‚
โ”œโ”€โ”€ e2e/                        Playwright E2E tests
โ”‚   โ””โ”€โ”€ auth.spec.js, posts.spec.js
โ”œโ”€โ”€ .github/workflows/ci.yml    GitHub Actions
โ”œโ”€โ”€ playwright.config.js
โ””โ”€โ”€ README.md

Development Setup

# Clone or initialise the monorepo
mkdir mern-blog && cd mern-blog

# Server
mkdir server && cd server
npm init -y
npm install express mongoose dotenv bcryptjs jsonwebtoken cors helmet \
  compression multer cloudinary multer-storage-cloudinary \
  nodemailer express-rate-limit socket.io express-validator
npm install --save-dev nodemon jest supertest mongodb-memory-server
cd ..

# Client
npm create vite@latest client -- --template react
cd client
npm install axios react-router-dom socket.io-client dompurify
npm install --save-dev vitest @testing-library/react @testing-library/jest-dom \
  @testing-library/user-event jsdom msw
cd ..

# E2E
npm install --save-dev @playwright/test
npx playwright install chromium

Common Mistakes

Mistake 1 โ€” Starting to code without a schema design

โŒ Wrong โ€” adding fields to models as you discover you need them:

// Adding likedBy to Post three weeks after building the Post model
// โ†’ requires a database migration, breaks existing data, wastes time

โœ… Correct โ€” design the complete schema upfront. It is much easier to add a field you planned than to retrofit one you forgot.

Mistake 2 โ€” Building all API routes before starting React

โŒ Wrong โ€” horizontal build: all Express, then all React, then integration.

โœ… Correct โ€” vertical build: register endpoint + register form + test end-to-end. Then login. Then post creation. Each vertical slice is testable and gives immediate feedback on API design flaws.

Mistake 3 โ€” Not planning the auth flow before writing a single line

โŒ Wrong โ€” building posts before auth is complete:

// Post routes require req.user โ€” but auth middleware is not yet built
// All post tests fail until auth is finished โ€” wasted work

โœ… Correct โ€” always build auth first. Every protected route depends on it. A working auth system with register, login, and protect middleware is the bedrock everything else stands on.

Quick Reference โ€” Build Order

Phase Build in This Order
1 โ€” Foundation Express setup, MongoDB connection, error handler, asyncHandler, AppError
2 โ€” Auth User model, register, login, protect middleware, AuthContext, login/register forms
3 โ€” Posts (core) Post model, CRUD API, PostList, PostDetail, CreatePost form
4 โ€” Media Multer + Cloudinary, avatar upload, cover image upload, ImageUploader component
5 โ€” Comments Comment model, comment API, Socket.io, live comment list, typing indicator
6 โ€” Email Nodemailer, email verification, password reset
7 โ€” Polish Search, pagination, tags, admin panel, dark mode, responsive layout
8 โ€” Launch Tests, CI/CD, Atlas, Render, Netlify

🧠 Test Yourself

You are starting the capstone and want to build it as efficiently as possible. Should you (A) build all Express routes first, then all React components, or (B) build one complete feature end-to-end (register works in the browser) before starting the next?