Intelligent Routing

MoroJS features an intelligent routing system that automatically optimizes middleware ordering, provides type-safe validation, and enhances performance through smart route compilation.

What is Intelligent Routing?

Traditional frameworks require you to manually order middleware and handle route compilation. MoroJS's intelligent routing system automatically:

  • Optimizes middleware order based on dependencies and performance characteristics
  • Compiles routes at startup for maximum runtime performance
  • Provides type safety throughout the entire request pipeline
  • Enables intelligent caching and rate limiting per route

Basic Route Definition

Simple Routes

typescript

1import { createApp } from '@morojs/moro';
2
3const app = createApp();
4
5// Simple GET route
6app.get('/', () => {
7  return { message: 'Hello World' };
8});
9
10// Route with parameters
11app.get('/users/:id', ({ params }) => {
12  return { userId: params.id };
13});
14
15// Multiple HTTP methods
16app.post('/users', ({ body }) => {
17  // Create user logic
18  return { success: true };
19});
20
21app.put('/users/:id', ({ params, body }) => {
22  // Update user logic
23  return { userId: params.id, updated: true };
24});

Automatic Type Inference

MoroJS automatically infers parameter types from your route patterns. The params.idin the example above is automatically typed as string.

Advanced Route Configuration

Route with Full Configuration

typescript

1import { Moro, z } from '@morojs/moro';
2
3const app = new Moro();
4
5// Define schemas
6const CreateUserSchema = z.object({
7  name: z.string().min(1).max(100),
8  email: z.string().email(),
9  age: z.number().min(18).max(120)
10});
11
12const UserParamsSchema = z.object({
13  id: z.string().uuid()
14});
15
16// Chainable API style - Recommended
17app.post('/users')
18  .body(CreateUserSchema)
19  .auth({ roles: ['admin'] })
20  .rateLimit({ requests: 10, window: 60000 })
21  .handler(async ({ body }) => {
22    // body is now fully typed and validated
23    const user = await createUser(body);
24    return {
25      id: user.id,
26      name: user.name,
27      email: user.email,
28      createdAt: user.createdAt
29    };
30  });
31
32// Schema-based routing style - For complex configs
33app.route({
34  method: 'POST',
35  path: '/users-alt',
36  validation: {
37    body: CreateUserSchema
38  },
39  auth: { roles: ['admin'] },
40  rateLimit: { requests: 10, window: 60000 },
41  handler: async ({ body }) => {
42    const user = await createUser(body);
43    return {
44      id: user.id,
45      name: user.name,
46      email: user.email,
47      createdAt: user.createdAt
48    };
49  }
50});

Intelligent Middleware Ordering

MoroJS automatically orders middleware based on dependencies. Authentication runs before rate limiting, validation happens early, and caching is optimally placed for maximum efficiency.

Route Patterns

Supported Route Patterns

typescript

1// Static routes
2app.get('/api/health').handler(() => ({ status: 'ok' }));
3
4// Named parameters
5app.get('/users/:id').handler(({ params }) => ({ userId: params.id }));
6app.get('/users/:id/posts/:postId').handler(({ params }) => ({ ...params }));
7
8// Optional parameters
9app.get('/posts/:id?').handler(({ params }) => ({ postId: params.id }));
10
11// Wildcard routes
12app.get('/files/*').handler(({ params }) => ({ path: params['*'] }));
13
14// Query parameters (automatically parsed)
15app.get('/search')
16  .query(z.object({
17    q: z.string(),
18    limit: z.coerce.number().default(10)
19  }))
20  .handler(({ query }) => {
21    return { results: search(query.q, query.limit) };
22  });
23
24// Route groups with common prefix
25app.group('/api/v1', (group) => {
26  group.get('/users').handler(getUsersHandler);
27  group.post('/users').handler(createUserHandler);
28  group.get('/users/:id').handler(getUserHandler);
29});

Intelligent Middleware System

Middleware Types and Ordering

typescript

1import { createApp } from '@morojs/moro';
2
3const app = createApp();
4
5// Global middleware (applied to all routes)
6app.use(async ({ request, response }, next) => {
7  console.log(`${request.method} ${request.url}`);
8  await next();
9});
10
11// Route with multiple middleware types - Chainable API
12app.get('/protected-data')
13  .use(async ({ headers }) => {
14    const token = headers.authorization?.replace('Bearer ', '');
15    if (!token) throw new Error('No token provided');
16    return { user: await verifyToken(token) };
17  })
18  .use(async ({ context }) => {
19    if (!context.user.hasPermission('read:data')) {
20      throw new Error('Insufficient permissions');
21    }
22  })
23  .cache({ ttl: 300000, key: (ctx) => `data:${ctx.user.id}` })
24  .rateLimit({ requests: 100, window: 3600000, key: (ctx) => ctx.user.id })
25  .handler(async ({ context }) => {
26    // All middleware has run, context is fully populated
27    return await getProtectedData(context.user);
28  });
29
30// Schema-based routing style
31app.route({
32  method: 'GET',
33  path: '/protected-data-alt',
34  middleware: [
35    async ({ headers }) => {
36      const token = headers.authorization?.replace('Bearer ', '');
37      if (!token) throw new Error('No token provided');
38      return { user: await verifyToken(token) };
39    },
40    async ({ context }) => {
41      if (!context.user.hasPermission('read:data')) {
42        throw new Error('Insufficient permissions');
43      }
44    }
45  ],
46  cache: { ttl: 300000 },
47  rateLimit: { requests: 100, window: 3600000 },
48  handler: async ({ context }) => {
49    return await getProtectedData(context.user);
50  }
51});

Automatic Ordering

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

Performance Benefits

  • No manual ordering needed
  • Optimal execution path
  • Early exit on failures
  • Compile-time optimization
  • Type-safe context passing

Route Compilation & Performance

Route Compilation Process

At application startup, MoroJS analyzes all your routes and creates an optimized execution plan:

  • Builds optimal middleware chains
  • Pre-compiles route matchers
  • Generates type-safe handlers
  • Optimizes for common patterns

Route Compilation Example

typescript

1// Development: Route definition with Chainable API
2app.get('/users/:id')
3  .params(z.object({ id: z.string().uuid() }))
4  .handler(({ params }) => getUserById(params.id));
5
6// Development: Route definition with Schema-based routing
7app.route({
8  method: 'GET',
9  path: '/users-alt/:id',
10  validation: {
11    params: z.object({ id: z.string().uuid() })
12  },
13  handler: ({ params }) => getUserById(params.id)
14});
15
16// Runtime: Both compile to optimized functions
17const compiledRoute = {
18  method: 'GET',
19  pattern: /^/users/([0-9a-f-]{36})$/,
20  paramNames: ['id'],
21  middlewareChain: [
22    validateParams,
23    executeHandler
24  ],
25  handler: (ctx) => {
26    // Pre-validated, type-safe execution
27    return getUserById(ctx.params.id);
28  }
29};

Best Practices

Do

  • • Use descriptive route patterns
  • • Define validation schemas
  • • Group related routes together
  • • Leverage automatic middleware ordering
  • • Use type-safe parameter extraction
  • • Define response schemas for documentation

Don't

  • • Manually order middleware unnecessarily
  • • Skip input validation
  • • Use overly complex route patterns
  • • Ignore TypeScript warnings
  • • Mix business logic in middleware
  • • Forget to handle errors properly

Next Steps