Summary
Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. This cheatsheet covers essential Express.js concepts including routing, middleware, request/response handling, error management, security practices, database integration, authentication, file uploads, testing, and performance optimization. Key features include unopinionated architecture, extensive middleware ecosystem, and rapid API development capabilities.
Express.js Basics
Installation & Setup
npm install express
Basic Server
const express = require('express');
const app = express();
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
ES6 Import Syntax
import express from 'express';
const app = express();
Routing
Basic Routes
// GET
app.get('/', (req, res) => {
res.send('Hello World');
});
// POST
app.post('/users', (req, res) => {
res.json({ message: 'User created' });
});
// PUT
app.put('/users/:id', (req, res) => {
res.json({ id: req.params.id });
});
// DELETE
app.delete('/users/:id', (req, res) => {
res.status(204).send();
});
Route Parameters
// Route params
app.get('/users/:id', (req, res) => {
const { id } = req.params; // { id: '123' }
});
// Query params
app.get('/search', (req, res) => {
const { q, limit } = req.query; // ?q=test&limit=10
});
// Multiple params
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
});
Route Patterns
// Wildcards
app.get('/files/*', handler); // matches /files/anything
// Optional params
app.get('/posts/:id?', handler); // matches /posts and /posts/123
// Regex
app.get(/.*fly$/, handler); // ends with 'fly'
Router Module
// routes/users.js
const router = express.Router();
router.get('/', (req, res) => res.json([]));
router.post('/', (req, res) => res.status(201).json({}));
module.exports = router;
// main.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
Middleware
Built-in Middleware
// Parse JSON bodies
app.use(express.json());
// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
// Serve static files
app.use(express.static('public'));
Custom Middleware
// Logger middleware
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // Pass control to next middleware
};
app.use(logger);
// Authentication middleware
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Verify token
req.user = { id: 1, name: 'John' };
next();
};
// Apply to specific routes
app.get('/protected', authenticate, (req, res) => {
res.json({ user: req.user });
});
Middleware Order
// Order matters!
app.use(express.json()); // Parse body first
app.use(logger); // Then log
app.use('/api', apiRoutes); // Then routes
app.use(errorHandler); // Error handler last
Request & Response
Request Object
app.get('/example', (req, res) => {
// Headers
req.headers['content-type'];
req.get('Authorization');
// Body (needs body parser)
req.body;
// Query & Params
req.query; // ?name=john
req.params; // /:id
// Cookies
req.cookies;
// URL info
req.url;
req.originalUrl;
req.path;
req.hostname;
req.ip;
req.protocol; // http or https
// Method
req.method; // GET, POST, etc.
});
Response Object
app.get('/example', (req, res) => {
// Send responses
res.send('Text');
res.json({ name: 'John' });
res.status(404).send('Not found');
// Headers
res.set('X-Custom-Header', 'value');
res.type('application/json');
// Redirects
res.redirect('/login');
res.redirect(301, '/new-url');
// Cookies
res.cookie('name', 'value', {
maxAge: 900000,
httpOnly: true
});
res.clearCookie('name');
// Downloads
res.download('/path/to/file.pdf');
res.sendFile('/absolute/path/to/file.html');
});
Error Handling
Synchronous Errors
app.get('/sync-error', (req, res) => {
throw new Error('Sync error'); // Express catches this
});
Asynchronous Errors
// Promises - use catch
app.get('/async-error', (req, res, next) => {
someAsyncOperation()
.then(result => res.json(result))
.catch(next); // Pass to error handler
});
// Async/await - use try-catch
app.get('/async-await', async (req, res, next) => {
try {
const result = await someAsyncOperation();
res.json(result);
} catch (error) {
next(error);
}
});
Error Handler Middleware
// Custom error class
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
}
}
// Error handler (must be last middleware)
app.use((err, req, res, next) => {
const { statusCode = 500, message } = err;
res.status(statusCode).json({
status: 'error',
statusCode,
message: err.isOperational ? message : 'Internal server error'
});
});
// Usage
app.get('/users/:id', (req, res, next) => {
const user = findUser(req.params.id);
if (!user) {
return next(new AppError('User not found', 404));
}
res.json(user);
});
Security Best Practices
Essential Security Middleware
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
// Security headers
app.use(helmet());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests
});
app.use('/api', limiter);
// Data sanitization
app.use(mongoSanitize()); // Prevent NoSQL injection
app.use(express.json({ limit: '10kb' })); // Body size limit
CORS Configuration
const cors = require('cors');
// Basic CORS
app.use(cors());
// Custom CORS
app.use(cors({
origin: 'https://example.com',
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
Environment Variables
require('dotenv').config();
const config = {
port: process.env.PORT || 3000,
dbUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET
};
// Never commit .env file!
Database Integration
MongoDB with Mongoose
const mongoose = require('mongoose');
// Connection
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Schema & Model
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, unique: true },
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
// CRUD Operations
app.post('/users', async (req, res, next) => {
try {
const user = await User.create(req.body);
res.status(201).json(user);
} catch (error) {
next(error);
}
});
SQL with Sequelize
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize(process.env.DATABASE_URL);
// Model
const User = sequelize.define('User', {
name: DataTypes.STRING,
email: { type: DataTypes.STRING, unique: true }
});
// Sync & Operations
await sequelize.sync();
const users = await User.findAll();
File Uploads
Using Multer
const multer = require('multer');
// Configure storage
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueName = Date.now() + '-' + file.originalname;
cb(null, uniqueName);
}
});
// File filter
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png'];
cb(null, allowedTypes.includes(file.mimetype));
};
const upload = multer({
storage,
fileFilter,
limits: { fileSize: 5 * 1024 * 1024 } // 5MB
});
// Routes
app.post('/upload', upload.single('avatar'), (req, res) => {
res.json({ filename: req.file.filename });
});
app.post('/upload-multiple', upload.array('photos', 5), (req, res) => {
res.json({ files: req.files });
});
Testing
Unit Testing with Jest
// app.js
const express = require('express');
const app = express();
app.get('/users', (req, res) => {
res.json([{ id: 1, name: 'John' }]);
});
module.exports = app;
// app.test.js
const request = require('supertest');
const app = require('./app');
describe('GET /users', () => {
it('should return users array', async () => {
const response = await request(app)
.get('/users')
.expect('Content-Type', /json/)
.expect(200);
expect(response.body).toBeInstanceOf(Array);
expect(response.body[0]).toHaveProperty('id');
});
});
Integration Testing
// Setup & Teardown
beforeAll(async () => {
await mongoose.connect(process.env.TEST_DB_URL);
});
afterAll(async () => {
await mongoose.connection.close();
});
// Test authenticated routes
describe('Protected Routes', () => {
let token;
beforeEach(async () => {
const user = await User.create(testUser);
token = generateToken(user);
});
it('should access protected route with token', async () => {
await request(app)
.get('/profile')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
});
Performance Optimization
Caching
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 }); // 10 min
// Cache middleware
const cacheMiddleware = (req, res, next) => {
const key = req.originalUrl;
const cached = cache.get(key);
if (cached) {
return res.json(cached);
}
// Store original json method
const originalJson = res.json;
res.json = (data) => {
cache.set(key, data);
originalJson.call(res, data);
};
next();
};
app.get('/expensive-operation', cacheMiddleware, handler);
Compression
const compression = require('compression');
app.use(compression({
level: 6,
threshold: 1024,
filter: (req, res) => {
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
}
}));
Database Optimization
// Indexing
userSchema.index({ email: 1 });
userSchema.index({ createdAt: -1 });
// Pagination
app.get('/users', async (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;
const users = await User.find()
.limit(limit)
.skip(skip)
.select('name email'); // Only select needed fields
res.json(users);
});
// Lean queries (Mongoose)
const users = await User.find().lean(); // Returns plain JS objects
Key Concepts & Patterns
Core Express Concepts
| Concept | Description | Usage |
|---|---|---|
| Middleware | Functions in request-response cycle | app.use(), modify req/res |
| Routing | URL pattern matching | app.get(), app.post(), etc. |
| Request Object | Contains request data | req.params, req.query, req.body |
| Response Object | Sends response to client | res.json(), res.send(), res.status() |
Method Comparison
| Method | Purpose | Scope | Example |
|---|---|---|---|
app.use() |
Mount middleware | All HTTP methods | app.use(express.json()) |
app.get() |
Handle GET requests | Specific HTTP method | app.get('/users', handler) |
app.all() |
Handle all HTTP methods | All methods, specific route | app.all('/admin/*', auth) |
router.use() |
Router-level middleware | Router scope | router.use(middleware) |
Response Methods Comparison
| Method | Purpose | Content-Type | Use Case |
|---|---|---|---|
res.send() |
Send any response | Auto-detected | General responses |
res.json() |
Send JSON response | application/json |
API responses |
res.render() |
Render template | text/html |
Template engines |
res.redirect() |
HTTP redirect | - | Navigation |
res.status() |
Set status code | - | Chain with other methods |
Error Handling Strategies
// Synchronous Error (Auto-caught)
app.get('/sync', (req, res) => {
throw new Error('Sync error'); // Express catches automatically
});
// Async Error (Manual handling)
app.get('/async', async (req, res, next) => {
try {
const data = await someAsyncOperation();
res.json(data);
} catch (error) {
next(error); // Pass to error handler
}
});
// Async Wrapper Pattern
const asyncWrapper = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Error Handler (must be last)
app.use((err, req, res, next) => {
res.status(err.statusCode || 500).json({
status: 'error',
message: err.message
});
});
Common Patterns
1. RESTful API Structure
// Resource-based routing
app.get('/api/users', getAllUsers); // GET all
app.get('/api/users/:id', getUser); // GET one
app.post('/api/users', createUser); // CREATE
app.put('/api/users/:id', updateUser); // UPDATE
app.delete('/api/users/:id', deleteUser); // DELETE
2. Middleware Chain Pattern
// Multiple middleware in sequence
app.get('/protected',
authenticate, // Verify token
authorize, // Check permissions
validateInput, // Validate data
controller // Business logic
);
3. Request Logging Pattern
const requestLogger = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);
});
next();
};
Best Practices & Architecture
Project Structure
project/
├── src/
│ ├── controllers/ # Business logic
│ ├── models/ # Data models
│ ├── routes/ # Route definitions
│ ├── middleware/ # Custom middleware
│ ├── utils/ # Helper functions
│ ├── config/ # Configuration files
│ └── app.js # Express app setup
├── tests/ # Test files
├── public/ # Static files
├── .env # Environment variables
├── .gitignore
└── package.json
Environment Configuration
// config/index.js
module.exports = {
development: {
port: 3000,
db: 'mongodb://localhost/dev',
jwtSecret: 'dev-secret'
},
production: {
port: process.env.PORT,
db: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET
}
}[process.env.NODE_ENV || 'development'];
Graceful Shutdown Pattern
const server = app.listen(PORT);
const gracefulShutdown = (signal) => {
console.log(`${signal} received`);
server.close(() => {
console.log('HTTP server closed');
// Close database connections
mongoose.connection.close();
process.exit(0);
});
};
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
Quick Reference
HTTP Status Codes Reference
| Code | Status | Use Case |
|---|---|---|
| 200 | OK | Successful GET, PUT |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Invalid input data |
| 401 | Unauthorized | Authentication required |
| 403 | Forbidden | Access denied |
| 404 | Not Found | Resource not found |
| 422 | Unprocessable Entity | Validation errors |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error |
Essential Packages
| Category | Package | Purpose |
|---|---|---|
| Core | express |
Web framework |
| Development | nodemon |
Auto-restart server |
| Security | helmet, cors |
Security headers, CORS |
| Auth | bcryptjs, jsonwebtoken |
Password hashing, JWT |
| Validation | express-validator, joi |
Input validation |
| Database | mongoose, sequelize |
MongoDB ODM, SQL ORM |
| File Upload | multer |
Handle multipart/form-data |
| Performance | compression |
Response compression |
| Rate Limiting | express-rate-limit |
API rate limiting |
| Environment | dotenv |
Environment variables |
| Logging | morgan, winston |
HTTP and application logging |
| Testing | jest, supertest |
Testing framework |
Debugging & Monitoring
// Debug mode
DEBUG=express:* node app.js
// Morgan HTTP logger
const morgan = require('morgan');
app.use(morgan('combined')); // or 'dev' for development
// Custom debug namespaces
const debug = require('debug')('app:server');
const dbDebug = require('debug')('app:db');
debug('Server starting...');
dbDebug('Database connected');
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'OK',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
Development Best Practices
✅ Error Handling: Always handle async errors, use proper error middleware
✅ Security: Implement helmet, rate limiting, input validation, CORS
✅ Code Organization: Use MVC pattern, separate concerns, modular routing
✅ Performance: Enable compression, implement caching, optimize database queries
✅ Testing: Write unit and integration tests, mock external dependencies
✅ Documentation: Document API endpoints, use tools like Swagger
Performance Optimization Checklist
- Enable gzip compression with
compressionmiddleware - Implement caching strategies (in-memory, Redis)
- Use connection pooling for databases
- Optimize database queries with indexes and pagination
- Implement rate limiting to prevent abuse
- Use CDN for static assets
- Enable HTTP/2 in production
- Monitor and profile application performance
Security Implementation Checklist
| Practice | Implementation | Purpose |
|---|---|---|
| Helmet | app.use(helmet()) |
Security headers |
| Rate Limiting | express-rate-limit |
Prevent abuse |
| Input Validation | express-validator |
Sanitize data |
| CORS | Configure properly | Control origins |
| Environment Variables | Use .env files |
Hide secrets |
| HTTPS | Force SSL in production | Encrypt data |
| JWT Security | Short expiry, secure storage | Token security |