Skip to content

路由模块化

通过路由模块化可以更好地组织大型应用的代码结构。

创建独立路由器

typescript
import { Router } from 'farrow-http'

// 用户路由模块
const userRouter = Router()

userRouter.get('/').use(() => {
  return Response.json({ users: getAllUsers() })
})

userRouter.get('/<id:int>').use((req) => {
  const user = getUserById(req.params.id)
  if (!user) {
    return Response.status(404).json({ error: '用户不存在' })
  }
  return Response.json(user)
})

userRouter.post('/', { body: CreateUserInput }).use((req) => {
  const user = createUser(req.body)
  return Response.status(201).json(user)
})

// 主应用挂载路由
const app = Http()
app.route('/api/users').use(userRouter)

URL 映射:

GET  /api/users     → userRouter.get('/')
GET  /api/users/123 → userRouter.get('/<id:int>')
POST /api/users     → userRouter.post('/')

嵌套路由

两层嵌套

typescript
const app = Http({ basenames: ['/api'] })

// 第一层: /api
const v1Router = app.route('/v1')        // /api/v1
const v2Router = app.route('/v2')        // /api/v2

// 第二层: /api/v1/...
const userRouter = Router()
const postRouter = Router()

v1Router.route('/users').use(userRouter)  // /api/v1/users
v1Router.route('/posts').use(postRouter)  // /api/v1/posts

// 定义路由
userRouter.get('/').use(() => {
  return Response.json({ users: [] })      // GET /api/v1/users
})

userRouter.get('/<id:int>').use((req) => {
  return Response.json({ userId: req.params.id })  // GET /api/v1/users/123
})

三层嵌套

typescript
const app = Http()

// 第一层
const apiRouter = app.route('/api')

// 第二层
const v1Router = apiRouter.route('/v1')

// 第三层
const resourcesRouter = v1Router.route('/resources')

// 第四层
const itemsRouter = resourcesRouter.route('/items')

itemsRouter.get('/').use(() => {
  return Response.json({ items: [] })
  // GET /api/v1/resources/items
})

itemsRouter.get('/<id:int>').use((req) => {
  return Response.json({ itemId: req.params.id })
  // GET /api/v1/resources/items/123
})

路由中间件继承

父路由的中间件会被子路由继承:

typescript
// 父路由的中间件会被子路由继承
const apiRouter = app.route('/api')
apiRouter.use(corsMiddleware)         // 所有 /api/* 都有 CORS
apiRouter.use(rateLimitMiddleware)    // 所有 /api/* 都有限流

const v1Router = apiRouter.route('/v1')
v1Router.use(authMiddleware)          // 所有 /api/v1/* 都需要认证

const userRouter = v1Router.route('/users')
userRouter.use(userValidationMiddleware)  // 所有 /api/v1/users/* 都有额外验证

// 请求 /api/v1/users 会依次经过:
// 1. corsMiddleware
// 2. rateLimitMiddleware
// 3. authMiddleware
// 4. userValidationMiddleware
// 5. 最终处理函数

中间件继承示例

typescript
const app = Http()

// 全局中间件
app.use(loggerMiddleware)
app.use(errorHandlerMiddleware)

// API 路由
const apiRouter = app.route('/api')
apiRouter.use(corsMiddleware)
apiRouter.use(rateLimitMiddleware)

// V1 路由
const v1Router = apiRouter.route('/v1')
v1Router.use(authMiddleware)

// 公开路由(不需要认证)
const publicRouter = apiRouter.route('/public')
// 只继承 corsMiddleware 和 rateLimitMiddleware

// 用户路由
const userRouter = v1Router.route('/users')
userRouter.use(userScopeMiddleware)

// 管理员路由
const adminRouter = v1Router.route('/admin')
adminRouter.use(adminOnlyMiddleware)

资源路由模式

REST 资源路由

typescript
function createResourceRouter<T>(
  resource: string,
  handlers: {
    list: () => T[]
    get: (id: number) => T | null
    create: (data: any) => T
    update: (id: number, data: any) => T | null
    delete: (id: number) => boolean
  }
) {
  const router = Router()

  // 列表
  router.get('/').use(() => {
    const items = handlers.list()
    return Response.json({ [resource]: items })
  })

  // 详情
  router.get('/<id:int>').use((req) => {
    const item = handlers.get(req.params.id)
    if (!item) {
      return Response.status(404).json({ error: `${resource} not found` })
    }
    return Response.json(item)
  })

  // 创建
  router.post('/').use((req) => {
    const item = handlers.create(req.body)
    return Response.status(201).json(item)
  })

  // 更新
  router.put('/<id:int>').use((req) => {
    const item = handlers.update(req.params.id, req.body)
    if (!item) {
      return Response.status(404).json({ error: `${resource} not found` })
    }
    return Response.json(item)
  })

  // 删除
  router.delete('/<id:int>').use((req) => {
    const success = handlers.delete(req.params.id)
    if (!success) {
      return Response.status(404).json({ error: `${resource} not found` })
    }
    return Response.status(204).empty()
  })

  return router
}

// 使用
const userRouter = createResourceRouter('users', {
  list: () => db.users.findAll(),
  get: (id) => db.users.findById(id),
  create: (data) => db.users.create(data),
  update: (id, data) => db.users.update(id, data),
  delete: (id) => db.users.delete(id)
})

app.route('/api/users').use(userRouter)

按功能分组

文件结构

src/
  routes/
    users/
      index.ts        # 用户路由入口
      handlers.ts     # 业务处理函数
      schemas.ts      # 数据验证 Schema
    posts/
      index.ts
      handlers.ts
      schemas.ts
    auth/
      index.ts
      handlers.ts
      schemas.ts
  app.ts              # 主应用

用户模块示例

typescript
// routes/users/schemas.ts
import { ObjectType, String, Int, Optional } from 'farrow-schema'

export class CreateUserInput extends ObjectType {
  name = String
  email = String
  age = Optional(Int)
}

export class UpdateUserInput extends ObjectType {
  name = Optional(String)
  email = Optional(String)
  age = Optional(Int)
}

// routes/users/handlers.ts
export async function listUsers() {
  return await db.users.findAll()
}

export async function getUser(id: number) {
  return await db.users.findById(id)
}

export async function createUser(data: TypeOf<typeof CreateUserInput>) {
  return await db.users.create(data)
}

export async function updateUser(id: number, data: TypeOf<typeof UpdateUserInput>) {
  return await db.users.update(id, data)
}

export async function deleteUser(id: number) {
  return await db.users.delete(id)
}

// routes/users/index.ts
import { Router } from 'farrow-http'
import * as handlers from './handlers'
import { CreateUserInput, UpdateUserInput } from './schemas'

export const userRouter = Router()

userRouter.get('/').use(async () => {
  const users = await handlers.listUsers()
  return Response.json({ users })
})

userRouter.get('/<id:int>').use(async (req) => {
  const user = await handlers.getUser(req.params.id)
  if (!user) {
    return Response.status(404).json({ error: 'User not found' })
  }
  return Response.json(user)
})

userRouter.post('/', { body: CreateUserInput }).use(async (req) => {
  const user = await handlers.createUser(req.body)
  return Response.status(201).json(user)
})

userRouter.put('/<id:int>', { body: UpdateUserInput }).use(async (req) => {
  const user = await handlers.updateUser(req.params.id, req.body)
  if (!user) {
    return Response.status(404).json({ error: 'User not found' })
  }
  return Response.json(user)
})

userRouter.delete('/<id:int>').use(async (req) => {
  const success = await handlers.deleteUser(req.params.id)
  if (!success) {
    return Response.status(404).json({ error: 'User not found' })
  }
  return Response.status(204).empty()
})

主应用组装

typescript
// app.ts
import { Http } from 'farrow-http'
import { userRouter } from './routes/users'
import { postRouter } from './routes/posts'
import { authRouter } from './routes/auth'

const app = Http()

// 全局中间件
app.use(loggerMiddleware)
app.use(errorHandlerMiddleware)

// API 路由
const apiRouter = app.route('/api')
apiRouter.use(corsMiddleware)

// 认证路由(公开)
apiRouter.route('/auth').use(authRouter)

// 受保护的路由
const protectedRouter = apiRouter.route('/v1')
protectedRouter.use(authMiddleware)

// 挂载资源路由
protectedRouter.route('/users').use(userRouter)
protectedRouter.route('/posts').use(postRouter)

// 启动服务器
app.listen(3000, () => {
  console.log('Server running on http://localhost:3000')
})

路由版本管理

同时支持多个版本

typescript
const app = Http()

// V1 路由
const v1Router = app.route('/api/v1')
v1Router.use(legacyMiddleware)

v1Router.get('/users').use(() => {
  return Response.json({ version: 'v1', users: getLegacyUsers() })
})

// V2 路由
const v2Router = app.route('/api/v2')
v2Router.use(modernMiddleware)

v2Router.get('/users').use(() => {
  return Response.json({ version: 'v2', users: getModernUsers() })
})

Header 版本控制

typescript
const app = Http()

app.use((req, next) => {
  const apiVersion = req.headers['x-api-version'] || 'v1'

  if (apiVersion === 'v1') {
    return v1Handler(req, next)
  } else if (apiVersion === 'v2') {
    return v2Handler(req, next)
  }

  return Response.status(400).json({
    error: 'Unsupported API version'
  })
})

动态路由加载

懒加载路由模块

typescript
app.route('/api/users').useLazy(async () => {
  const { userRouter } = await import('./routes/users')
  return userRouter
})

app.route('/api/posts').useLazy(async () => {
  const { postRouter } = await import('./routes/posts')
  return postRouter
})

条件加载

typescript
// 只在开发环境加载调试路由
if (process.env.NODE_ENV === 'development') {
  app.route('/debug').useLazy(async () => {
    const { debugRouter } = await import('./routes/debug')
    return debugRouter
  })
}

// 根据配置加载功能模块
if (config.features.admin) {
  app.route('/admin').useLazy(async () => {
    const { adminRouter } = await import('./routes/admin')
    return adminRouter
  })
}

路由文档生成

自动生成路由表

typescript
function generateRouteDocs(app: HttpPipeline) {
  const routes: Array<{
    method: string
    path: string
    description?: string
  }> = []

  // 遍历路由收集信息
  // (需要自定义实现)

  return routes
}

// 提供文档端点
app.get('/api/docs').use(() => {
  const routes = generateRouteDocs(app)
  return Response.json({ routes })
})

最佳实践

✅ 1. 清晰的目录结构

src/
  routes/          # 路由模块
    users/
    posts/
    auth/
  middleware/      # 中间件
    auth.ts
    cors.ts
    logger.ts
  services/        # 业务逻辑
    user.service.ts
    post.service.ts
  models/          # 数据模型
    user.model.ts
    post.model.ts
  app.ts           # 主应用

✅ 2. 单一职责

每个路由模块只负责一个资源或功能域。

✅ 3. 中间件分层

typescript
// 全局中间件
app.use(logger)
app.use(errorHandler)

// API 中间件
apiRouter.use(cors)
apiRouter.use(rateLimit)

// 认证中间件
protectedRouter.use(auth)

// 资源特定中间件
userRouter.use(userValidation)

✅ 4. 统一错误处理

在父路由层面统一处理错误,避免在每个路由中重复。

✅ 5. 版本管理

使用 URL 路径进行版本管理,保持向后兼容。

typescript
// ✅ 好
/api/v1/users
/api/v2/users

// ❌ 不好(难以管理)
/api/users (version in header)

这是一个第三方 Farrow 文档站 | 用 ❤️ 和 TypeScript 构建