路由模块化
通过路由模块化可以更好地组织大型应用的代码结构。
创建独立路由器
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)