Difficulty: Intermediate
The Express ecosystem includes hundreds of middleware packages that extend your application with common functionality. Instead of writing logging, security headers, CORS handling, or compression from scratch, you install well-tested packages and register them with app.use(). Understanding the most important third-party middleware is essential for building production-ready Express applications.
cors (Cross-Origin Resource Sharing) is needed when your frontend and backend run on different domains or ports, which is the case in virtually every modern web application during development. By default, browsers block cross-origin requests for security. The cors middleware adds the necessary Access-Control-Allow-Origin and related headers to your responses, telling the browser which origins are allowed to make requests. You can configure it to allow all origins, specific origins, or dynamically determine allowed origins.
morgan is an HTTP request logger that outputs information about every incoming request: the method, URL, status code, response time, and content length. It supports predefined formats ('dev', 'combined', 'common', 'short', 'tiny') and custom formats. In development, the 'dev' format provides color-coded output. In production, the 'combined' format (Apache-style) is standard for log analysis tools.
helmet secures your Express app by setting various HTTP response headers. It sets X-Content-Type-Options to prevent MIME sniffing, X-Frame-Options to prevent clickjacking, Content-Security-Policy to prevent XSS, and many more. Helmet is a collection of smaller middleware functions that each set a specific header. You should use it in every production application - it is a simple one-line addition that significantly improves security.
compression applies gzip or deflate compression to response bodies, reducing transfer size by 60-80% for text-based content like JSON, HTML, CSS, and JavaScript. The browser automatically decompresses the response, so this is transparent to the client. For production applications serving substantial amounts of data, compression middleware dramatically improves load times and reduces bandwidth costs.
const express = require('express');
const cors = require('cors');
const app = express();
// Allow all origins (development)
app.use(cors());
// OR: Configure specific origins (production)
// app.use(cors({
// origin: ['https://myapp.com', 'https://admin.myapp.com'],
// methods: ['GET', 'POST', 'PUT', 'DELETE'],
// allowedHeaders: ['Content-Type', 'Authorization'],
// credentials: true // Allow cookies
// }));
// OR: Dynamic origin based on a whitelist
// const whitelist = ['https://myapp.com', 'http://localhost:5173'];
// app.use(cors({
// origin: (origin, callback) => {
// if (!origin || whitelist.includes(origin)) {
// callback(null, true);
// } else {
// callback(new Error('Not allowed by CORS'));
// }
// }
// }));
app.get('/api/data', (req, res) => {
res.json({ message: 'This is accessible from any origin' });
});
app.listen(3000, () => console.log('Server on port 3000'));
cors() with no arguments allows all origins. In production, always specify allowed origins explicitly. The credentials option must be true if your frontend sends cookies or Authorization headers. Dynamic origin allows runtime decisions based on a whitelist.
const express = require('express');
const morgan = require('morgan');
const helmet = require('helmet');
const compression = require('compression');
const app = express();
// Security headers (should be first)
app.use(helmet());
// Compress responses
app.use(compression());
// Request logging
if (process.env.NODE_ENV === 'production') {
app.use(morgan('combined')); // Apache-style logs
} else {
app.use(morgan('dev')); // Colorized development logs
}
// Body parsing
app.use(express.json());
app.get('/api/data', (req, res) => {
// This response will be compressed automatically
res.json({
message: 'Hello World',
items: Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
name: `Item ${i + 1}`,
description: 'A sample item for testing compression'
}))
});
});
app.listen(3000, () => console.log('Server on port 3000'));
// Morgan 'dev' output example:
// GET /api/data 200 3.456 ms - 4523
Register helmet() first for maximum security. compression() should come before route handlers so all responses are compressed. Morgan logs request details - 'dev' format shows METHOD URL STATUS TIME SIZE with color coding.
const express = require('express');
const cookieParser = require('cookie-parser');
const rateLimit = require('express-rate-limit');
const app = express();
// Parse cookies from request headers
app.use(cookieParser('my-secret')); // Secret for signed cookies
// Rate limiting - max 100 requests per 15 minutes per IP
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Max 100 requests per window
message: { error: 'Too many requests, please try again later' },
standardHeaders: true, // Send RateLimit-* headers
legacyHeaders: false // Disable X-RateLimit-* headers
});
// Apply rate limiting to all API routes
app.use('/api', limiter);
// Stricter limit for auth routes
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // Only 5 login attempts per 15 minutes
message: { error: 'Too many login attempts' }
});
app.use(express.json());
app.post('/api/login', authLimiter, (req, res) => {
// Set a cookie
res.cookie('session', 'abc123', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 24 * 60 * 60 * 1000 // 1 day
});
res.json({ message: 'Logged in' });
});
app.get('/api/profile', (req, res) => {
// Read cookies
const session = req.cookies.session;
res.json({ session: session || 'No session cookie' });
});
app.listen(3000, () => console.log('Server on port 3000'));
cookie-parser reads cookies from the Cookie header and makes them available on req.cookies. express-rate-limit prevents abuse by limiting requests per IP address. Different rate limits can be applied to different routes - stricter limits for authentication endpoints prevent brute-force attacks.
cors, morgan, helmet, compression, cookie-parser, rate-limiting