阿里云主机折上折
  • 微信号
Current Site:Index > Routing splitting solution for large-scale projects

Routing splitting solution for large-scale projects

Author:Chuan Chen 阅读数:50030人阅读 分类: Node.js

Background of Route Splitting

As project scale increases, a single routing file can become bloated and difficult to maintain. In a typical Koa2 project's initial phase, all routes might be written in app.js. When the number of routes exceeds 50, the file can grow to thousands of lines of code. In such cases, locating specific routes becomes challenging, team collaboration may lead to conflicts, and test cases become harder to pinpoint.

Basic Route Splitting Solution

The most straightforward approach is to split routes by business modules. For example, in an e-commerce project, the following directory structure can be created:

routes/
  ├── user.js
  ├── product.js
  ├── order.js
  └── cart.js

Each file exports its own router instance:

// routes/user.js
const router = require('koa-router')();

router.get('/users', async (ctx) => {
  ctx.body = await UserService.list();
});

router.post('/users', async (ctx) => {
  ctx.body = await UserService.create(ctx.request.body);
});

module.exports = router;

Then, load them uniformly in the main file:

// app.js
const userRouter = require('./routes/user');
const productRouter = require('./routes/product');

app.use(userRouter.routes());
app.use(productRouter.routes());

Automated Route Loading

When the number of route files increases, manually requiring each file becomes tedious. Use the fs module to load routes automatically:

const fs = require('fs');
const path = require('path');

const routesPath = path.join(__dirname, 'routes');
fs.readdirSync(routesPath).forEach(file => {
  const router = require(path.join(routesPath, file));
  app.use(router.routes());
});

Multi-Level Route Structure

For more complex projects, a multi-level route structure may be needed. For example, separating admin and user APIs:

routes/
  ├── admin/
  │   ├── user.js
  │   └── system.js
  └── api/
      ├── v1/
      │   ├── user.js
      │   └── product.js
      └── v2/
          ├── user.js
          └── order.js

Implement this by using prefix in sub-routes:

// routes/api/v1/user.js
const router = require('koa-router')({
  prefix: '/api/v1'
});

router.get('/users', ...);

Route Middleware Isolation

Different routes may require different middleware combinations. Configure middleware at the route level:

// routes/admin/user.js
const auth = require('../../middlewares/admin-auth');
const router = require('koa-router')();

router.use(auth);
router.get('/users', ...);

Dynamic Route Registration

Some scenarios require dynamic route registration based on configuration:

// routes/dynamic.js
module.exports = function(config) {
  const router = require('koa-router')();
  
  config.routes.forEach(route => {
    router[route.method](route.path, ...route.middlewares);
  });
  
  return router;
}

Route Metadata Management

Add metadata to routes for unified management:

// decorators/route.js
function GET(path) {
  return function(target, name, descriptor) {
    const handler = descriptor.value;
    handler.__route = {
      method: 'get',
      path,
      handler
    };
    return descriptor;
  }
}

// routes/user.js
class UserController {
  @GET('/users')
  async listUsers(ctx) {
    // ...
  }
}

Performance Optimization Considerations

When the number of routes grows, matching performance may degrade. Solutions include:

  1. Prioritizing high-frequency routes
  2. Using route caching
  3. Avoiding complex regex paths
// Handle high-frequency routes separately
const frequentRouter = require('koa-router')();
frequentRouter.get('/api/products/hot', ...);
app.use(frequentRouter.routes());

// Other routes
app.use(mainRouter.routes());

Test-Friendly Design

For easier testing, route files should:

  1. Export the raw router instance
  2. Avoid auto-registering middleware
  3. Support dependency injection
// routes/user.js
module.exports = function(userService) {
  const router = require('koa-router')();
  
  router.get('/users', async ctx => {
    ctx.body = await userService.list();
  });
  
  return router;
}

Version Control Solutions

For API versioning, consider these approaches:

  1. URL path versioning
// routes/v1/user.js
router.prefix('/v1/users');
  1. Request header versioning
router.get('/users', ctx => {
  const version = ctx.headers['x-api-version'] || 'v1';
  // Return different data based on version
});
  1. Query parameter versioning
router.get('/users', ctx => {
  const version = ctx.query.version || 'v1';
});

Route Documentation Generation

Generate route documentation using JSDoc:

/**
 * @api {get} /users Get user list
 * @apiVersion 1.0.0
 * @apiName GetUsers
 */
router.get('/users', ...);

Use tools to parse comments and generate API documentation.

Unified Error Handling

Define error handling for each route file:

// routes/user.js
router.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = err.status || 500;
    ctx.body = { 
      error: 'User module error',
      details: err.message 
    };
  }
});

Route Permission Control

Implement fine-grained permission control:

// routes/admin.js
const permissions = {
  '/users': ['admin'],
  '/settings': ['superadmin']
};

router.use(async (ctx, next) => {
  const userRole = ctx.state.user.role;
  const requiredRoles = permissions[ctx.path];
  
  if (!requiredRoles.includes(userRole)) {
    ctx.throw(403);
  }
  
  await next();
});

Route Performance Monitoring

Add route-level performance tracking:

router.use(async (ctx, next) => {
  const start = Date.now();
  await next();
  const duration = Date.now() - start;
  metrics.track(ctx.path, duration);
});

Route Caching Strategy

Set caching based on route characteristics:

// routes/product.js
const cache = require('../middlewares/cache');

router.get('/products/:id', 
  cache({ ttl: 3600 }),
  async ctx => {
    // ...
  }
);

Route Traffic Control

Limit access to high-frequency routes:

const rateLimit = require('koa-ratelimit');

router.get('/api/search',
  rateLimit({
    duration: 60000,
    max: 30
  }),
  async ctx => {
    // ...
  }
);

Route Parameter Validation

Validate parameters using libraries like Joi:

const Joi = require('joi');

router.post('/users', 
  validate({
    body: Joi.object({
      name: Joi.string().required(),
      email: Joi.string().email().required()
    })
  }),
  async ctx => {
    // ...
  }
);

Microservice Route Gateway

Act as a gateway router in microservice architectures:

const { proxy } = require('koa-http-proxy');

router.all('/user-service/(.*)',
  proxy({
    target: 'http://user-service:3000',
    rewrite: path => path.replace('/user-service', '')
  })
);

Route Configuration Center

Load routes dynamically from a configuration center:

const configClient = require('config-client');

router.use(async (ctx, next) => {
  const routes = await configClient.get('routes');
  const routeConfig = routes.find(r => r.path === ctx.path);
  
  if (routeConfig?.maintenance) {
    ctx.status = 503;
    return;
  }
  
  await next();
});

Route A/B Testing

Support A/B testing at the route level:

router.get('/new-feature', 
  abTest({
    variants: [
      { weight: 50, handler: variantAHandler },
      { weight: 50, handler: variantBHandler }
    ]
  })
);

Route Data Mocking

Mock route data in development environments:

const isDev = process.env.NODE_ENV === 'development';

router.get('/products',
  isDev ? mockHandler : realHandler
);

function mockHandler(ctx) {
  ctx.body = [{ id: 1, name: 'Mock Product' }];
}

Route Request Transformation

Uniformly process request data:

router.use(async (ctx, next) => {
  // Convert camelCase to snake_case
  if (ctx.request.body) {
    ctx.request.body = convertKeys(ctx.request.body);
  }
  await next();
});

Route Response Formatting

Standardize response formats:

router.use(async (ctx, next) => {
  await next();
  
  if (ctx.body && !ctx.body.error) {
    ctx.body = {
      code: 200,
      data: ctx.body,
      timestamp: Date.now()
    };
  }
});

Route Health Checks

Add health check endpoints:

router.get('/health', (ctx) => {
  ctx.body = {
    status: 'UP',
    services: {
      db: checkDb(),
      cache: checkCache()
    }
  };
});

Route Deprecation Marking

Mark routes for deprecation:

router.get('/old-api',
  deprecated({
    message: 'Use /new-api instead',
    sunset: '2023-12-31'
  }),
  oldApiHandler
);

Route Experimental Features

Isolate experimental features:

router.get('/experimental/feature-x',
  experimental({
    flag: 'FEATURE_X_ENABLED'
  }),
  experimentalHandler
);

Route Traffic Tagging

Tag traffic for specific routes:

router.use('/admin', (ctx, next) => {
  ctx.state.tags = ctx.state.tags || [];
  ctx.state.tags.push('admin');
  return next();
});

Route Data Masking

Automatically mask sensitive data:

router.use('/users', (ctx, next) => {
  await next();
  if (ctx.body?.email) {
    ctx.body.email = maskEmail(ctx.body.email);
  }
});

Route Request Recording

Record production requests:

router.use((ctx, next) => {
  if (shouldRecord(ctx.path)) {
    requestRecorder.record(ctx);
  }
  return next();
});

Route Intelligent Degradation

Automatically degrade during exceptions:

router.get('/recommendations',
  circuitBreaker({
    fallback: getCachedRecommendations
  }),
  getLiveRecommendations
);

Route Blue-Green Deployment

Support blue-green deployment switching:

router.get('/products',
  blueGreen({
    blue: blueHandler,
    green: greenHandler,
    selector: ctx => ctx.headers['x-deployment-group']
  })
);

Route Geo-Routing

Route requests based on region:

router.get('/content',
  geoRouter({
    'US': usContentHandler,
    'EU': euContentHandler,
    'default': globalContentHandler
  })
);

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.