To secure a MERN Stack application, you need to: (1) use helmet for security headers and express-mongo-sanitize to prevent NoSQL injection, (2) implement proper JWT authentication middleware, (3) use mongoose schema validation for input sanitization, (4) configure CORS properly with specific origins, and (5) always get user IDs from auth middleware (never from request body). This blueprint covers Express security middleware with MongoDB protection.
TL;DR
MERN Stack requires careful attention to NoSQL injection, Express middleware security, and JWT handling. Use mongoose with schema validation, sanitize all inputs, implement proper CORS, and store JWTs securely. Never trust client-provided user IDs.
Express Security Middleware Express
import express from 'express'
import helmet from 'helmet'
import cors from 'cors'
import rateLimit from 'express-rate-limit'
import mongoSanitize from 'express-mongo-sanitize'
const app = express()
// Security headers
app.use(helmet())
// CORS configuration
app.use(cors({
origin: process.env.CLIENT_URL,
credentials: true,
}))
// Rate limiting
app.use(rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
}))
// Prevent NoSQL injection
app.use(mongoSanitize())
app.use(express.json({ limit: '10kb' }))
JWT Authentication Middleware Node.js
import jwt from 'jsonwebtoken'
export const protect = async (req, res, next) => {
const token = req.cookies.token || req.headers.authorization?.split(' ')[1]
if (!token) {
return res.status(401).json({ error: 'Not authorized' })
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET)
req.user = await User.findById(decoded.id).select('-password')
if (!req.user) {
return res.status(401).json({ error: 'User not found' })
}
next()
} catch (error) {
return res.status(401).json({ error: 'Invalid token' })
}
}
Mongoose Schema Validation MongoDB
import mongoose from 'mongoose'
const postSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'Title is required'],
maxlength: [200, 'Title cannot exceed 200 characters'],
trim: true,
},
content: {
type: String,
required: true,
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
}, { timestamps: true })
export default mongoose.model('Post', postSchema)
Protected Route Example Express
import express from 'express'
import { protect } from '../middleware/auth.js'
import Post from '../models/Post.js'
const router = express.Router()
router.post('/', protect, async (req, res) => {
const { title, content } = req.body
const post = await Post.create({
title,
content,
author: req.user._id, // Use verified user from middleware
})
res.status(201).json(post)
})
router.get('/mine', protect, async (req, res) => {
const posts = await Post.find({ author: req.user._id })
res.json(posts)
})
Never trust client IDs. Always use req.user from your auth middleware, never req.body.userId or similar client-provided values.
Security Checklist
Pre-Launch Checklist
Helmet middleware enabled
CORS properly configured
Rate limiting implemented
express-mongo-sanitize used
JWT secret is strong and in env
User IDs from auth middleware only
Alternative Stacks
Consider these related blueprints:
- MEAN Stack - Angular frontend alternative
- Next.js + Supabase + Vercel - PostgreSQL/Supabase alternative
- T3 Stack - TypeScript-first alternative