API
定义新路由
创建新 API 端点、校验与 OpenAPI 元数据的逐步指南。
本指南介绍如何在 MuseMVP 中添加新的 API 路由,遵循现有的校验、中间件与 OpenAPI 文档模式。
逐步流程
先写失败测试,在 packages/db/src/ 更新 schema 与 query(第一个参数为 db),再通过应用 database 包装绑定;业务编排放 src/modules/<feature>/lib/。
在 src/backend/api/routes/ 中创建新 Hono 路由或扩展现有路由。
在 src/backend/api/app.ts 中注册路由。
在 src/backend/api-client/ 中添加 API client hooks 供前端使用。
1. 定义路由处理器
创建新路由文件或扩展现有文件。使用 Zod 做校验,describeRoute 做 OpenAPI 元数据。
/** New file: src/backend/api/routes/example/router.ts */
import { Hono } from "hono";
import { describeRoute, resolver, validator } from "hono-openapi";
import { z } from "zod";
import { authMiddleware } from "@/backend/api/middleware/auth";
const querySchema = z.object({
limit: z.coerce.number().int().min(1).max(100).default(10),
offset: z.coerce.number().int().min(0).default(0),
});
const responseSchema = z.object({
items: z.array(z.object({ id: z.string(), name: z.string() })),
total: z.number(),
});
export const exampleRouter = new Hono()
.basePath("/example")
.use(authMiddleware)
.get(
"/",
validator("query", querySchema),
describeRoute({
tags: ["Example"],
summary: "List items",
responses: {
200: {
description: "Items returned",
content: {
"application/json": {
schema: resolver(responseSchema),
},
},
},
},
}),
async (c) => {
const { limit, offset } = c.req.valid("query");
const user = c.get("user");
/** Call the owning feature service using validated input. */
return c.json({ items: [], total: 0 });
},
);2. 注册路由
在 src/backend/api/app.ts 中添加新路由:
import { exampleRouter } from "./routes/example/router";
const appRouter = app
.route("/", authRouter)
.route("/", exampleRouter) // 添加此行
// ... 其他路由3. 校验模式
| 位置 | 用途 | 示例 |
|---|---|---|
validator("query", schema) | 查询参数 | 分页、搜索 |
validator("json", schema) | JSON 请求体 | 创建/更新 payload |
validator("form", schema) | 表单数据 | 联系表单、文件上传 |
validator("param", schema) | 路径参数 | /conversations/:id |
4. 中间件选项
| 中间件 | 使用场景 |
|---|---|
authMiddleware | 需要已登录用户;设置 c.get("user") 和 c.get("session") |
adminMiddleware | 需要 admin 角色;用于仅管理员端点 |
localeMiddleware | 从请求/cookie 设置 c.get("locale") 用于 i18n |
5. API Client Hook
创建调用新端点的 TanStack Query hook:
/** New file: src/backend/api-client/example/use-example.ts */
import { useQuery } from "@tanstack/react-query";
import { apiClient } from "@/backend/api-client/api-client";
export function useExample(params?: { limit?: number; offset?: number }) {
return useQuery({
queryKey: ["example", params],
queryFn: async () => {
const query = {
...(params?.limit !== undefined ? { limit: String(params.limit) } : {}),
...(params?.offset !== undefined ? { offset: String(params.offset) } : {}),
};
const res = await apiClient.example.$get({ query });
if (!res.ok) throw new Error("Fetch failed");
return res.json();
},
});
}参考实现
src/backend/api/routes/admin/users.ts
src/backend/api/routes/contact/router.ts
src/backend/api/routes/assets/router.ts
- 管理路由:
admin/users.ts—adminMiddleware、query 校验、describeRoute - 公开路由:
contact/router.ts—localeMiddleware、表单校验 - 鉴权路由:
assets/router.ts—authMiddleware、query + json 校验、describeRoute
新增路由同时补 unit/API 契约,检查未登录/权限/非法参数/响应错误,页面旅程变化更新 tests/e2e/SCENARIOS.md。同步应用页面与 API 副本并运行 pnpm apps:parity。