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 (req, res, next) => {
7  const start = Date.now();
8  console.log(`${req.method} ${req.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 (req, res, next) => {
18  res.headers.set('Access-Control-Allow-Origin', '*');
19  res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
20  res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
21  
22  await next();
23});
24
25// Error handling middleware
26app.use(async (err, req, res, next) => {
27  res.status = 500;
28  res.body = {
29    error: 'INTERNAL_SERVER_ERROR',
30    message: 'Something went wrong',
31    details: err.message
32  };
33});

Route-Specific Middleware

Authentication Middleware

typescript

1const authMiddleware = async (req, res, next) => {
2  const token = req.headers.authorization?.replace('Bearer ', '');
3  if (!token) {
4    res.status = 401;
5    res.body = { error: 'Authentication required' };
6    return;
7  }
8  
9  const user = await verifyJWT(token);
10  req.user = user; // Attach user to request
11  await next();
12};
13
14// Chainable API style
15app.get('/profile')
16  .use(authMiddleware)
17  .handler((req, res) => {
18    // req.user is now available and typed
19    return { profile: req.user };
20  });
21
22// Schema-based routing style
23app.route({
24  method: 'GET',
25  path: '/profile-alt',
26  middleware: [authMiddleware],
27  handler: (req, res) => {
28    return { profile: req.user };
29  }
30});

Multiple Middleware - Chainable API

typescript

1// Chainable API style - recommended for simple cases
2app.post('/admin/users')
3  .use(authMiddleware)
4  .use(async (req, res, next) => {
5    if (req.user.role !== 'admin') {
6      res.status = 403;
7      res.body = { error: 'Admin access required' };
8      return;
9    }
10    await next();
11  })
12  .rateLimit({ requests: 10, window: 60000 })
13  .use(async (req, res, next) => {
14    await logAdminAction(req.user.id, 'CREATE_USER', req.url);
15    await next();
16  })
17  .body(CreateUserSchema)
18  .handler((req, res) => {
19    // All middleware has run, req.user is populated
20    return createUser(req.body, req.user);
21  });

Built-in Middleware

MoroJS includes several built-in middleware options:

Built-in Middleware Options - Chainable API

typescript

1// Chainable API style
2app.post('/api/data')
3  .rateLimit({ 
4    requests: 100, 
5    window: 3600000, // 1 hour in ms
6    key: (req) => req.user?.id || req.ip 
7  })
8  .cache({ 
9    ttl: 300000, // 5 minutes in ms
10    key: (req) => `user:${req.user.id}`,
11    tags: ['user-data']
12  })
13  .handler((req, res) => {
14    return { data: processData(req.body) };
15  });
16
17// Schema-based routing style
18app.route({
19  method: 'POST',
20  path: '/api/data-alt',
21  rateLimit: { 
22    requests: 100, 
23    window: 3600000 
24  },
25  cache: { 
26    ttl: 300000 
27  },
28  handler: (req, res) => {
29    return { data: processData(req.body) };
30  }
31});

Creating Custom Middleware

Custom Middleware Pattern

typescript

1// Middleware function signature
2type MiddlewareFunction = (
3  req: HttpRequest,
4  res: HttpResponse,
5  next: () => Promise<void>
6) => Promise<void>;
7
8// Example: Request timing middleware
9const timingMiddleware: MiddlewareFunction = async (req, res, next) => {
10  const start = performance.now();
11  
12  // Add timing data to request
13  req.timing = { start };
14  
15  await next();
16  
17  const end = performance.now();
18  req.timing.duration = end - start;
19  
20  // Add timing header to response
21  res.headers.set('X-Response-Time', `${req.timing.duration}ms`);
22};
23
24// Usage - Chainable API
25app.get('/timed-endpoint')
26  .use(timingMiddleware)
27  .handler((req, res) => {
28    return { 
29      message: 'Success',
30      processingTime: req.timing.duration 
31    };
32  });
33
34// Usage - Schema-based routing
35app.route({
36  method: 'GET',
37  path: '/timed-endpoint-alt',
38  middleware: [timingMiddleware],
39  handler: (req, res) => {
40    return { 
41      message: 'Success',
42      processingTime: req.timing.duration 
43    };
44  }
45});

Conditional Middleware

typescript

1// Middleware that runs conditionally
2const conditionalAuth = async (req, res, next) => {
3  const isPublicEndpoint = req.url.includes('/public/');
4  
5  if (!isPublicEndpoint) {
6    // Only authenticate non-public endpoints
7    const token = req.headers.authorization;
8    if (!token) {
9      res.status = 401;
10      res.body = { error: 'Authentication required' };
11      return;
12    }
13    req.user = await verifyToken(token);
14  }
15  
16  await next();
17};
18
19// Middleware factory
20const createRoleMiddleware = (requiredRole: string) => {
21  return async (req, res, next) => {
22    if (!req.user || req.user.role !== requiredRole) {
23      res.status = 403;
24      res.body = { error: `${requiredRole} role required` };
25      return;
26    }
27    await next();
28  };
29};
30
31// Chainable API
32app.get('/admin/stats')
33  .use(conditionalAuth)
34  .use(createRoleMiddleware('admin'))
35  .handler((req, res) => getAdminStats());
36
37// Schema-based routing
38app.route({
39  method: 'GET',
40  path: '/admin/stats-alt',
41  middleware: [
42    conditionalAuth,
43    createRoleMiddleware('admin')
44  ],
45  handler: (req, res) => getAdminStats()
46});

Next Steps