Middleware API Reference

Complete reference for MoroJS middleware system. Learn about creating, composing, and managing middleware with intelligent ordering.

Global Middleware

Global middleware runs on every request. MoroJS automatically orders middleware for optimal performance and security.

app.use() - Global Middleware (Actual Implementation)

typescript

1import { createApp } from '@morojs/moro';
2
3const app = createApp();
4
5// Simple middleware function
6app.use((req, res, next) => {
7  console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
8  next();
9});
10
11// Example from enterprise-app
12app.use((req, res, next) => {
13  console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
14  next();
15});
16
17// Middleware is simpler in actual implementation
18app.use((req, res, next) => {
19  // Add request ID for tracking
20  req.requestId = Date.now().toString();
21  next();
22});

Intelligent Ordering

MoroJS automatically orders middleware based on type and dependencies:

  1. Security middleware (CORS, Helmet)
  2. Request parsing (body parser, compression)
  3. Authentication middleware
  4. Rate limiting middleware
  5. Custom business logic middleware
  6. Error handling middleware

Route-Specific Middleware

Route Middleware Configuration

typescript

1// Single middleware
2app.get('/protected', {
3  middleware: [authMiddleware],
4  handler: (context) => ({ message: 'Protected resource' })
5});
6
7// Multiple middleware with ordering
8app.post('/api/users', {
9  middleware: [
10    validateApiKey,
11    rateLimitMiddleware({ max: 10, window: '1m' }),
12    requireAuth,
13    requireRole('admin')
14  ],
15  body: CreateUserSchema,
16  handler: createUserHandler
17});
18
19// Conditional middleware
20app.get('/data/:id', {
21  middleware: [
22    (context, next) => {
23      if (context.params.id === 'public') {
24        return next(); // Skip auth for public data
25      }
26      return authMiddleware(context, next);
27    }
28  ],
29  handler: getDataHandler
30});

Creating Custom Middleware

Basic Middleware Pattern

typescript

1// Basic middleware function
2const loggingMiddleware = async (context, next) => {
3  const start = Date.now();
4  
5  console.log(`[${new Date().toISOString()}] ${context.request.method} ${context.request.url}`);
6  
7  await next();
8  
9  const duration = Date.now() - start;
10  console.log(`Request completed in ${duration}ms`);
11};
12
13// Middleware with configuration
14const rateLimitMiddleware = (options = {}) => {
15  const { max = 100, window = '15m' } = options;
16  
17  return async (context, next) => {
18    const key = `rate_limit:${context.ip}`;
19    const current = await cache.get(key) || 0;
20    
21    if (current >= max) {
22      return context.status(429).json({ 
23        error: 'Rate limit exceeded',
24        retryAfter: window
25      });
26    }
27    
28    await cache.set(key, current + 1, { ttl: window });
29    await next();
30  };
31};

Advanced Middleware Patterns

typescript

1// Error handling middleware
2const errorHandlerMiddleware = async (context, next) => {
3  try {
4    await next();
5  } catch (error) {
6    console.error('Request error:', error);
7    
8    if (error.name === 'ValidationError') {
9      return context.status(400).json({
10        error: 'Validation failed',
11        details: error.details
12      });
13    }
14    
15    return context.status(500).json({
16      error: 'Internal server error'
17    });
18  }
19};
20
21// Authentication middleware
22const authMiddleware = async (context, next) => {
23  const token = context.headers.authorization?.replace('Bearer ', '');
24  
25  if (!token) {
26    return context.status(401).json({ error: 'Authentication required' });
27  }
28  
29  try {
30    const user = await verifyJWT(token);
31    context.user = user; // Add to context
32    await next();
33  } catch (error) {
34    return context.status(401).json({ error: 'Invalid token' });
35  }
36};
37
38// CORS middleware
39const corsMiddleware = (options = {}) => {
40  const {
41    origin = '*',
42    methods = ['GET', 'POST', 'PUT', 'DELETE'],
43    headers = ['Content-Type', 'Authorization']
44  } = options;
45  
46  return async (context, next) => {
47    context.response.headers.set('Access-Control-Allow-Origin', origin);
48    context.response.headers.set('Access-Control-Allow-Methods', methods.join(', '));
49    context.response.headers.set('Access-Control-Allow-Headers', headers.join(', '));
50    
51    if (context.request.method === 'OPTIONS') {
52      return context.status(200).text('');
53    }
54    
55    await next();
56  };
57};

Middleware Context

Context Object Properties

typescript

1interface MiddlewareContext {
2  // HTTP primitives
3  request: Request;
4  response: Response;
5  
6  // Parsed request data
7  params: Record<string, string>;
8  query: Record<string, string>;
9  headers: Record<string, string>;
10  body: any;
11  
12  // Request metadata
13  ip: string;
14  userAgent: string;
15  method: string;
16  url: string;
17  
18  // Shared context (for middleware communication)
19  context: Record<string, any>;
20  
21  // Response helpers
22  json(data: any): Response;
23  text(data: string): Response;
24  status(code: number): ResponseBuilder;
25  redirect(url: string, status?: number): Response;
26  
27  // Utilities
28  set(key: string, value: any): void;
29  get(key: string): any;
30}

Context Usage Examples

typescript

1// Sharing data between middleware
2const dataMiddleware = async (context, next) => {
3  context.set('startTime', Date.now());
4  context.set('requestId', generateId());
5  await next();
6};
7
8const loggingMiddleware = async (context, next) => {
9  const requestId = context.get('requestId');
10  console.log(`[${requestId}] Processing request`);
11  await next();
12  
13  const duration = Date.now() - context.get('startTime');
14  console.log(`[${requestId}] Completed in ${duration}ms`);
15};
16
17// Modifying request/response
18const transformMiddleware = async (context, next) => {
19  // Transform request body
20  if (context.body) {
21    context.body = transformRequestData(context.body);
22  }
23  
24  await next();
25  
26  // Transform response (if needed)
27  // Note: Response transformation is handled differently
28};

Built-in Middleware

Available Built-in Middleware

typescript

1import { 
2  cors,
3  rateLimit,
4  helmet,
5  compression,
6  bodyParser,
7  staticFiles,
8  logger
9} from '@morojs/moro/middleware';
10
11const app = createApp();
12
13// CORS middleware
14app.use(cors({
15  origin: ['https://example.com'],
16  credentials: true,
17  methods: ['GET', 'POST', 'PUT', 'DELETE']
18}));
19
20// Rate limiting
21app.use(rateLimit({
22  max: 100,
23  window: '15m',
24  keyGenerator: (context) => context.ip
25}));
26
27// Security headers
28app.use(helmet({
29  contentSecurityPolicy: {
30    directives: {
31      defaultSrc: ["'self'"],
32      styleSrc: ["'self'", "'unsafe-inline'"]
33    }
34  }
35}));
36
37// Response compression
38app.use(compression({
39  threshold: 1024,
40  filter: (context) => context.response.headers.get('content-type')?.includes('json')
41}));
42
43// Request logging
44app.use(logger({
45  format: 'combined',
46  skip: (context) => context.request.url.includes('/health')
47}));

Security Middleware

  • cors() - Cross-Origin Resource Sharing
  • helmet() - Security headers
  • rateLimit() - Request rate limiting
  • csrf() - CSRF protection

Utility Middleware

  • compression() - Response compression
  • bodyParser() - Request body parsing
  • staticFiles() - Static file serving
  • logger() - Request logging

Next Steps