--- name: middleware-patterns description: Express middleware patterns and best practices for GoodGo microservices. Use when creating custom middleware, organizing middleware chains, handling request/response transformation, or implementing cross-cutting concerns. --- # Middleware Patterns ## When to Use This Skill Use this skill when: - Creating custom Express middleware - Organizing middleware chains and ordering - Implementing authentication/authorization middleware - Creating request/response transformation middleware - Handling cross-cutting concerns (logging, metrics, validation) - Implementing async middleware patterns - Testing middleware implementations ## Core Concepts ### Middleware Function Signature Express middleware functions have this signature: ```typescript (req: Request, res: Response, next: NextFunction) => void | Promise ``` ### Middleware Types 1. **Application-level**: Applied to all routes (`app.use()`) 2. **Router-level**: Applied to specific routes (`router.use()`) 3. **Route-level**: Applied to specific route handlers ### Middleware Execution Order **Critical**: Middleware order matters! Execution flows top-to-bottom: ``` Request → Middleware 1 → Middleware 2 → ... → Route Handler → Response ``` ## Patterns ### Middleware Chain Order Standard middleware order in GoodGo services: ```typescript // 1. Security (Helmet, CORS) app.use(helmet()); app.use(cors({ ... })); // 2. Rate Limiting app.use('/api', rateLimitMiddleware); // 3. Correlation ID (early for tracing) app.use(correlationMiddleware()); // 4. Body Parsing app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); // 5. Request Logging app.use(loggerMiddleware); // 6. Metrics app.use(metricsMiddleware); // 7. Routes app.use(createRouter()); // 8. Error Handling (ALWAYS LAST) app.use(notFoundHandler); app.use(errorHandler); ``` ### Correlation Middleware Pattern Adds correlation ID for request tracing: ```typescript export const correlationMiddleware = () => { return (req: Request, res: Response, next: NextFunction) => { const correlationId = req.headers['x-correlation-id'] || generateId(); req.correlationId = correlationId; res.setHeader('x-correlation-id', correlationId); next(); }; }; ``` ### Authentication Middleware Pattern Verifies JWT tokens and attaches user to request: ```typescript export const authenticate = () => { return async (req: Request, res: Response, next: NextFunction) => { try { const token = extractToken(req); if (!token) { return res.status(401).json({ error: 'Unauthorized' }); } const payload = await jwtService.verify(token); req.user = payload; next(); } catch (error) { return res.status(401).json({ error: 'Invalid token' }); } }; }; ``` ### Validation Middleware Pattern Validates request data using Zod: ```typescript export const validateDto = (schema: AnyZodObject, property: 'body' | 'query' | 'params' = 'body') => { return (req: Request, res: Response, next: NextFunction) => { try { const validatedData = schema.parse(req[property]); (req as any)[property] = validatedData; next(); } catch (error) { if (error instanceof ZodError) { return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', details: error.errors, }, }); } next(error); } }; }; ``` ### Conditional Middleware Apply middleware conditionally: ```typescript const conditionalAuth = (options: { optional?: boolean } = {}) => { return async (req: Request, res: Response, next: NextFunction) => { try { const token = extractToken(req); if (token) { const payload = await jwtService.verify(token); req.user = payload; } else if (!options.optional) { return res.status(401).json({ error: 'Unauthorized' }); } next(); } catch (error) { if (!options.optional) { return res.status(401).json({ error: 'Invalid token' }); } next(); } }; }; ``` ### Async Middleware Pattern Handle async operations properly: ```typescript export const asyncMiddleware = (fn: Function) => { return (req: Request, res: Response, next: NextFunction) => { Promise.resolve(fn(req, res, next)).catch(next); }; }; // Usage app.get('/users', asyncMiddleware(async (req, res) => { const users = await userService.findAll(); res.json({ success: true, data: users }); })); ``` ### Request/Response Transformation Transform request or response data: ```typescript export const transformResponse = () => { return (req: Request, res: Response, next: NextFunction) => { const originalJson = res.json.bind(res); res.json = function(data: any) { const transformed = { success: true, data, timestamp: new Date().toISOString(), }; return originalJson(transformed); }; next(); }; }; ``` ### Logging Middleware Pattern Log request details: ```typescript export const requestLogger = (req: Request, res: Response, next: NextFunction) => { const startTime = Date.now(); res.on('finish', () => { const duration = Date.now() - startTime; logger.info('Request completed', { method: req.method, url: req.url, statusCode: res.statusCode, duration, correlationId: req.correlationId, }); }); next(); }; ``` ## Best Practices 1. **Order Matters**: Place middleware in correct order (security → correlation → parsing → logging → routes → errors) 2. **Error Handling**: Always handle errors and call `next(error)` for error middleware 3. **Async Support**: Wrap async middleware properly to catch promise rejections 4. **Early Returns**: Use early returns for validation failures (don't call `next()`) 5. **Request Extension**: Use TypeScript declaration merging to extend Request type 6. **Conditional Logic**: Use middleware factories for conditional middleware 7. **Reusability**: Create reusable middleware functions 8. **Performance**: Keep middleware lightweight, avoid heavy operations ## Common Mistakes 1. **Wrong Order**: Placing middleware in incorrect order (e.g., error handler before routes) 2. **Not Calling Next**: Forgetting to call `next()` or `next(error)` 3. **Async Errors**: Not handling promise rejections in async middleware 4. **Early Return Issues**: Calling `next()` after sending response 5. **Type Safety**: Not extending Express Request type properly 6. **Performance**: Doing heavy operations in middleware ## Troubleshooting ### Middleware Not Executing **Problem**: Middleware not being called **Solution**: Check middleware order, ensure it's added before routes. Verify `next()` is called. ### Async Errors Not Caught **Problem**: Unhandled promise rejections in async middleware **Solution**: Use `asyncHandler` wrapper or wrap async code in try-catch with `next(error)`. ## Resources - [Correlation Middleware](../../services/iam-service/src/middlewares/correlation.middleware.ts) - [Auth Middleware](../../services/iam-service/src/middlewares/auth.middleware.ts) - [Validation Middleware](../../services/iam-service/src/middlewares/validation.middleware.ts) - [Error Handling](../error-handling-patterns/SKILL.md) - Error middleware patterns