Middleware Architecture and Connect
Understanding middleware pattern for request processing
Challenge
Section titled “Challenge”Your server needs to log requests, authenticate users, parse JSON, handle errors, and process responses. How do you compose all these concerns without creating a monolithic request handler?
Explanation
Section titled “Explanation”Middleware is a function that processes requests/responses sequentially. Express and other frameworks are built on this pattern, allowing you to compose concerns as pluggable modules.
Middleware Pattern
Section titled “Middleware Pattern”const http = require('http');
// Middleware functionfunction middleware(req, res, next) { // Do something console.log(`${req.method} ${req.url}`);
// Call next middleware next();}
// Simple middleware stackfunction createApp(middlewares = []) { return http.createServer(async (req, res) => { let index = 0;
function next() { if (index >= middlewares.length) { if (!res.headersSent) { res.writeHead(404); res.end('Not Found'); } return; }
const middleware = middlewares[index++]; middleware(req, res, next); }
next(); });}
// Using middlewareconst app = createApp([ // Logging middleware (req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); },
// Authentication middleware (req, res, next) => { if (req.headers.authorization) { req.user = { authenticated: true }; next(); } else { res.writeHead(401); res.end('Unauthorized'); } },
// Main handler (req, res, next) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'OK', user: req.user })); }]);
app.listen(3000);Express Middleware Pattern
Section titled “Express Middleware Pattern”const express = require('express');const app = express();
// Built-in middlewareapp.use(express.json()); // Parse JSONapp.use(express.urlencoded()); // Parse forms
// Custom middlewareapp.use((req, res, next) => { req.startTime = Date.now(); next();});
// Error handling middleware (4 parameters)app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ error: 'Internal Server Error' });});
// Route-specific middlewareapp.get('/secure', authenticate, (req, res) => { res.json({ data: 'protected' });});
function authenticate(req, res, next) { if (req.headers.authorization) { next(); } else { res.status(401).json({ error: 'Unauthorized' }); }}Practical Middleware Examples
Section titled “Practical Middleware Examples”// Session middlewarefunction sessionMiddleware(req, res, next) { const sessionId = req.headers['x-session-id']; req.session = sessions.get(sessionId) || {};
res.on('finish', () => { sessions.set(sessionId, req.session); });
next();}
// Request loggingfunction 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();}
// Request size limitfunction limitRequestSize(maxSize) { return (req, res, next) => { let size = 0; req.on('data', (chunk) => { size += chunk.length; if (size > maxSize) { res.writeHead(413); res.end('Payload Too Large'); req.destroy(); } }); next(); };}
// CORS middlewarefunction cors(req, res, next) { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); } else { next(); }}Error Handling Middleware
Section titled “Error Handling Middleware”// Error wrapper for async routesfunction asyncHandler(fn) { return (req, res, next) => { Promise.resolve(fn(req, res, next)) .catch(next); // Pass errors to error middleware };}
// Error middlewareapp.use((err, req, res, next) => { console.error(err);
if (err instanceof ValidationError) { res.status(400).json({ error: err.message, field: err.field }); } else if (err instanceof NotFoundError) { res.status(404).json({ error: err.message }); } else { res.status(500).json({ error: 'Internal Server Error' }); }});
// Custom errorsclass ValidationError extends Error { constructor(message, field) { super(message); this.name = 'ValidationError'; this.field = field; }}