Middleware System

MoroJS features an intelligent middleware system that automatically orders execution, provides type-safe context passing, and optimizes performance.

How Middleware Works

Middleware in MoroJS runs in a predictable, optimized order:

Execution Order

  1. 1Request parsing
  2. 2Authentication
  3. 3Authorization
  4. 4Validation
  5. 5Rate limiting
  6. 6Caching
  7. 7Handler execution

Benefits

  • Automatic optimization
  • Type-safe context
  • Early exit on errors
  • Composable design
  • Performance optimized

Global Middleware

Global middleware runs on every request and is perfect for logging, CORS, and other cross-cutting concerns:

Global Middleware Example

typescript

1import { createApp } from '@morojs/moro';
2
3const app = createApp();
4
5// Logging middleware
6app.use(async ({ request }, next) => {
7  const start = Date.now();
8  console.log(`${request.method} ${request.url}`);
9  
10  await next();
11  
12  const duration = Date.now() - start;
13  console.log(`Request completed in ${duration}ms`);
14});
15
16// CORS middleware
17app.use(async ({ response }, next) => {
18  response.headers.set('Access-Control-Allow-Origin', '*');
19  response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
20  response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
21  
22  await next();
23});
24
25// Error handling middleware
26app.use(async (context, next) => {
27  try {
28    await next();
29  } catch (error) {
30    return {
31      status: 500,
32      body: {
33        error: 'INTERNAL_SERVER_ERROR',
34        message: 'Something went wrong'
35      }
36    };
37  }
38});

Route-Specific Middleware

Authentication Middleware

typescript

1const authMiddleware = async ({ headers }) => {
2  const token = headers.authorization?.replace('Bearer ', '');
3  if (!token) {
4    throw new Error('Authentication required');
5  }
6  
7  const user = await verifyJWT(token);
8  return { user }; // Added to context
9};
10
11app.get('/profile', {
12  middleware: [authMiddleware],
13  handler: ({ context }) => {
14    // context.user is now available and typed
15    return { profile: context.user };
16  }
17});

Multiple Middleware

typescript

1app.post('/admin/users', {
2  middleware: [
3    // 1. Authentication
4    authMiddleware,
5    
6    // 2. Authorization
7    async ({ context }) => {
8      if (context.user.role !== 'admin') {
9        throw new Error('Admin access required');
10      }
11    },
12    
13    // 3. Rate limiting
14    { rateLimit: { max: 10, window: '1m' } },
15    
16    // 4. Audit logging
17    async ({ context, request }) => {
18      await logAdminAction(context.user.id, 'CREATE_USER', request.url);
19    }
20  ],
21  
22  body: CreateUserSchema,
23  handler: ({ body, context }) => {
24    // All middleware has run, context is populated
25    return createUser(body, context.user);
26  }
27});

Built-in Middleware

MoroJS includes several built-in middleware options:

Built-in Middleware Options

typescript

1app.post('/api/data', {
2  middleware: [
3    // Rate limiting
4    { 
5      rateLimit: { 
6        max: 100, 
7        window: '1h',
8        key: (ctx) => ctx.user?.id || ctx.ip 
9      } 
10    },
11    
12    // Caching
13    { 
14      cache: { 
15        ttl: '5m',
16        key: (ctx) => `user:${ctx.user.id}`,
17        tags: ['user-data']
18      } 
19    },
20    
21    // Request size limits
22    { 
23      bodyLimit: '10mb' 
24    },
25    
26    // Timeout protection
27    { 
28      timeout: '30s' 
29    }
30  ],
31  
32  handler: ({ body }) => {
33    return { data: processData(body) };
34  }
35});

Creating Custom Middleware

Custom Middleware Pattern

typescript

1// Middleware function signature
2type MiddlewareFunction = (
3  context: RequestContext,
4  next: () => Promise<void>
5) => Promise<any>;
6
7// Example: Request timing middleware
8const timingMiddleware: MiddlewareFunction = async (context, next) => {
9  const start = performance.now();
10  
11  // Add timing data to context
12  context.timing = { start };
13  
14  await next();
15  
16  const end = performance.now();
17  context.timing.duration = end - start;
18  
19  // Add timing header to response
20  context.response.headers.set('X-Response-Time', `${context.timing.duration}ms`);
21};
22
23// Usage
24app.get('/timed-endpoint', {
25  middleware: [timingMiddleware],
26  handler: ({ context }) => {
27    return { 
28      message: 'Success',
29      processingTime: context.timing.duration 
30    };
31  }
32});

Conditional Middleware

typescript

1// Middleware that runs conditionally
2const conditionalAuth = async (context, next) => {
3  const isPublicEndpoint = context.request.url.includes('/public/');
4  
5  if (!isPublicEndpoint) {
6    // Only authenticate non-public endpoints
7    const token = context.headers.authorization;
8    if (!token) {
9      throw new Error('Authentication required');
10    }
11    context.user = await verifyToken(token);
12  }
13  
14  await next();
15};
16
17// Middleware factory
18const createRoleMiddleware = (requiredRole: string) => {
19  return async ({ context }) => {
20    if (!context.user || context.user.role !== requiredRole) {
21      throw new Error(`${requiredRole} role required`);
22    }
23  };
24};
25
26app.get('/admin/stats', {
27  middleware: [
28    conditionalAuth,
29    createRoleMiddleware('admin')
30  ],
31  handler: () => getAdminStats()
32});

Next Steps