- Spec files
npm-express
Describes: pkg:npm/express@4.21.x
- Description
- Fast, unopinionated, minimalist web framework for Node.js
- Author
- tessl
- Last updated
middleware.md docs/
1# Middleware System23Middleware functions for request processing, authentication, logging, body parsing, and request/response modification in the Express request-response cycle.45## Capabilities67### Middleware Mounting89Mount middleware functions to execute during request processing with optional path filtering.1011```javascript { .api }12/**13* Mount middleware function(s) at optional path14* @param {string} [path] - Optional path to mount middleware (default: '/')15* @param {...RequestHandler} middleware - Middleware function(s) to mount16* @returns {Application} Application instance for chaining17*/18app.use(path?: string, ...middleware: RequestHandler[]): Application;1920/**21* Middleware function signature22* @param {Request} req - Express request object23* @param {Response} res - Express response object24* @param {NextFunction} next - Function to pass control to next middleware25*/26type RequestHandler = (req: Request, res: Response, next: NextFunction) => void | Promise<void>;2728/**29* Error handling middleware signature30* @param {Error} err - Error object31* @param {Request} req - Express request object32* @param {Response} res - Express response object33* @param {NextFunction} next - Function to pass control to next middleware34*/35type ErrorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => void;36```3738**Usage Examples:**3940```javascript41const app = express();4243// Global middleware (runs for all routes)44app.use((req, res, next) => {45console.log(`${req.method} ${req.url} - ${new Date()}`);46next();47});4849// Path-specific middleware50app.use('/api', (req, res, next) => {51req.apiVersion = 'v1';52next();53});5455// Multiple middleware functions56app.use('/admin', authenticate, authorize, (req, res, next) => {57res.locals.isAdmin = true;58next();59});6061// Error handling middleware (must be last)62app.use((err, req, res, next) => {63console.error(err.stack);64res.status(500).send('Something broke!');65});66```6768### Built-in Body Parsers6970Parse incoming request bodies into JavaScript objects accessible via `req.body`.7172```javascript { .api }73/**74* Parse JSON request bodies75* @param {JsonOptions} [options] - JSON parser options76* @returns {RequestHandler} Middleware function77*/78express.json(options?: JsonOptions): RequestHandler;7980/**81* Parse URL-encoded request bodies (form data)82* @param {UrlencodedOptions} [options] - URL-encoded parser options83* @returns {RequestHandler} Middleware function84*/85express.urlencoded(options?: UrlencodedOptions): RequestHandler;8687/**88* Parse raw request bodies into Buffer89* @param {RawOptions} [options] - Raw parser options90* @returns {RequestHandler} Middleware function91*/92express.raw(options?: RawOptions): RequestHandler;9394/**95* Parse text request bodies into string96* @param {TextOptions} [options] - Text parser options97* @returns {RequestHandler} Middleware function98*/99express.text(options?: TextOptions): RequestHandler;100101/**102* JSON parser options103*/104interface JsonOptions {105/** Request size limit (default: '100kb') */106limit?: string | number;107/** Inflate compressed bodies (default: true) */108inflate?: boolean;109/** Only parse objects and arrays (default: true) */110strict?: boolean;111/** Custom reviver function */112reviver?: (key: string, value: any) => any;113/** Verify function for raw body */114verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;115}116117/**118* URL-encoded parser options119*/120interface UrlencodedOptions {121/** Request size limit (default: '100kb') */122limit?: string | number;123/** Use extended syntax with qs library (default: true) */124extended?: boolean;125/** Maximum parameter count (default: 1000) */126parameterLimit?: number;127/** Verify function for raw body */128verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;129}130131/**132* Raw parser options133*/134interface RawOptions {135/** Request size limit (default: '100kb') */136limit?: string | number;137/** Expected content type (default: 'application/octet-stream') */138type?: string | string[] | ((req: Request) => boolean);139/** Verify function for raw body */140verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;141}142143/**144* Text parser options145*/146interface TextOptions {147/** Request size limit (default: '100kb') */148limit?: string | number;149/** Text encoding (default: 'utf8') */150defaultCharset?: string;151/** Expected content type (default: 'text/plain') */152type?: string | string[] | ((req: Request) => boolean);153/** Verify function for raw body */154verify?: (req: Request, res: Response, buf: Buffer, encoding?: string) => void;155}156```157158**Usage Examples:**159160```javascript161const app = express();162163// Parse JSON bodies164app.use(express.json());165166// Parse URL-encoded bodies (form data)167app.use(express.urlencoded({ extended: true }));168169// Parse raw bodies for specific routes170app.use('/upload', express.raw({ type: 'application/octet-stream', limit: '10mb' }));171172// Parse text bodies173app.use('/webhook', express.text({ type: 'text/plain' }));174175// Custom JSON parsing with options176app.use(express.json({177limit: '50mb',178verify: (req, res, buf, encoding) => {179// Verify request body before parsing180req.rawBody = buf;181}182}));183184// Routes can now access req.body185app.post('/api/users', (req, res) => {186console.log(req.body); // Parsed JSON object187res.json({ received: req.body });188});189```190191### Static File Serving192193Serve static files from a directory with caching and security options.194195```javascript { .api }196/**197* Serve static files from root directory198* @param {string} root - Root directory to serve files from199* @param {StaticOptions} [options] - Static serving options200* @returns {RequestHandler} Middleware function201*/202express.static(root: string, options?: StaticOptions): RequestHandler;203204/**205* Static file serving options206*/207interface StaticOptions {208/** Enable dot files (default: false) */209dotfiles?: 'allow' | 'deny' | 'ignore';210/** Set ETag header (default: true) */211etag?: boolean;212/** File extensions to try (default: false) */213extensions?: string[] | false;214/** Fallthrough to next handler on 404 (default: true) */215fallthrough?: boolean;216/** Immutable cache control (default: false) */217immutable?: boolean;218/** Default file to serve for directories (default: 'index.html') */219index?: string | string[] | false;220/** Last-Modified header (default: true) */221lastModified?: boolean;222/** Cache-Control max-age in milliseconds (default: 0) */223maxAge?: number | string;224/** Redirect to trailing slash (default: true) */225redirect?: boolean;226/** Function to set custom headers */227setHeaders?: (res: Response, path: string, stat: any) => void;228}229```230231**Usage Examples:**232233```javascript234const app = express();235const path = require('path');236237// Serve static files from 'public' directory238app.use(express.static('public'));239240// Serve from specific path241app.use('/static', express.static(path.join(__dirname, 'assets')));242243// Multiple static directories244app.use(express.static('public'));245app.use(express.static('uploads'));246247// Static files with options248app.use('/files', express.static('documents', {249dotfiles: 'deny',250index: false,251maxAge: '1d',252setHeaders: (res, path) => {253if (path.endsWith('.pdf')) {254res.set('Content-Type', 'application/pdf');255}256}257}));258```259260### Query String Parsing261262Create custom query string parsing middleware. Note: Express has built-in query parsing, so this is only needed for custom parsing logic.263264```javascript { .api }265/**266* Create query string parsing middleware267* @param {QueryOptions | Function} [options] - Query parser options or custom parser function268* @returns {RequestHandler} Middleware function269*/270express.query(options?: QueryOptions | Function): RequestHandler;271272/**273* Query parser options274*/275interface QueryOptions {276/** Maximum number of parameters (default: 1000) */277parameterLimit?: number;278/** Array format parsing */279arrayFormat?: 'brackets' | 'indices' | 'comma' | 'separator';280/** Delimiter for arrays */281delimiter?: string;282/** Parse numbers (default: false) */283parseNumbers?: boolean;284/** Parse booleans (default: false) */285parseBooleans?: boolean;286}287```288289**Usage Examples:**290291```javascript292const app = express();293294// Express automatically parses query strings (no middleware needed)295// GET /search?q=express&category=web&limit=10296app.get('/search', (req, res) => {297console.log(req.query);298// { q: 'express', category: 'web', limit: '10' }299res.json(req.query);300});301302// Only use express.query for custom parsing logic303app.use('/custom', express.query((str, options) => {304// Custom query string parsing logic305return customQueryParser(str);306}));307308// GET /products?active=true&price=99&tags=web,framework309app.get('/products', (req, res) => {310console.log(req.query);311// { active: true, price: 99, tags: 'web,framework' }312res.json(req.query);313});314```315316### Parameter Middleware317318Middleware that executes when specific route parameters are encountered.319320```javascript { .api }321/**322* Add callback triggers for route parameters323* @param {string} name - Parameter name to trigger on324* @param {ParamHandler} handler - Parameter handler function325* @returns {Application} Application instance for chaining326*/327app.param(name: string, handler: ParamHandler): Application;328329/**330* Parameter handler function signature331* @param {Request} req - Express request object332* @param {Response} res - Express response object333* @param {NextFunction} next - Next middleware function334* @param {any} value - Parameter value from URL335* @param {string} name - Parameter name336*/337type ParamHandler = (req: Request, res: Response, next: NextFunction, value: any, name: string) => void;338```339340**Usage Examples:**341342```javascript343// Parameter middleware for user loading344app.param('userId', async (req, res, next, id) => {345try {346const user = await User.findById(id);347if (!user) {348return res.status(404).json({ error: 'User not found' });349}350req.user = user;351next();352} catch (error) {353next(error);354}355});356357// Parameter validation middleware358app.param('id', (req, res, next, id) => {359if (!/^\d+$/.test(id)) {360return res.status(400).json({ error: 'ID must be numeric' });361}362next();363});364365// Routes using the parameters366app.get('/users/:userId', (req, res) => {367// req.user is already loaded368res.json(req.user);369});370371app.get('/posts/:id', (req, res) => {372// ID is already validated373res.json({ id: req.params.id });374});375```376377### Custom Middleware Patterns378379Common middleware patterns for cross-cutting concerns.380381**Usage Examples:**382383```javascript384// Request logging middleware385const logger = (req, res, next) => {386const start = Date.now();387res.on('finish', () => {388const duration = Date.now() - start;389console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);390});391next();392};393394// Authentication middleware395const authenticate = (req, res, next) => {396const token = req.headers.authorization?.split(' ')[1];397if (!token) {398return res.status(401).json({ error: 'No token provided' });399}400401jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {402if (err) {403return res.status(401).json({ error: 'Invalid token' });404}405req.user = decoded;406next();407});408};409410// CORS middleware411const cors = (req, res, next) => {412res.header('Access-Control-Allow-Origin', '*');413res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');414res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');415416if (req.method === 'OPTIONS') {417return res.sendStatus(200);418}419next();420};421422// Rate limiting middleware423const rateLimit = (windowMs, max) => {424const requests = new Map();425426return (req, res, next) => {427const ip = req.ip;428const now = Date.now();429430if (!requests.has(ip)) {431requests.set(ip, []);432}433434const ipRequests = requests.get(ip);435const recentRequests = ipRequests.filter(time => now - time < windowMs);436437if (recentRequests.length >= max) {438return res.status(429).json({ error: 'Too many requests' });439}440441recentRequests.push(now);442requests.set(ip, recentRequests);443next();444};445};446447// Use middleware448app.use(logger);449app.use(cors);450app.use('/api', rateLimit(15 * 60 * 1000, 100)); // 100 requests per 15 minutes451app.use('/protected', authenticate);452```453454## Middleware Execution Order455456Middleware executes in the order it's defined, making order important for proper functionality.457458```javascript459const app = express();460461// 1. First: CORS headers462app.use(cors);463464// 2. Second: Request logging465app.use(logger);466467// 3. Third: Body parsing468app.use(express.json());469470// 4. Fourth: Authentication (for protected routes)471app.use('/api/protected', authenticate);472473// 5. Fifth: Route handlers474app.get('/api/users', getUsers);475476// 6. Last: Error handling477app.use(errorHandler);478```