Unified Platform Host

App API 总览

原生客户端与 Web 应用共用的版本化 /api/app/v1 后端。

模板内置一套版本化 App API/api/app/v1)——功能完备的 HTTP 后端,供 Swift、Kotlin、Expo、Electron 与 Tauri 直接调用。Next.js、Nuxt 与 TanStack Start 实现相同公开契约。Web 页面与客户端共享同一个 Better Auth 用户、同一个数据库、同一条订阅:在 iOS 上用 Web 注册的账户登录、读取同一份数据,并且任一渠道购买都会解锁同一份 权益。

提供的能力

  • /api/app/v1 下的 15 个业务端点:Profile、Todos、Device Token、APNs 测试推送、订阅快照 + 管理入口、presigned 上传、账户删除。
  • 一个 Service 层src/server/app/),路由处理器与 Web 页面共同调用—— 业务逻辑只写一次。
  • 所有端点均使用 Bearer Session 鉴权;资源归属始终来自服务端 Session, 绝不接受客户端提交的用户 ID。
  • 机器可读的 OpenAPI 3.1 契约docs/openapi.yaml

认证本身没有/api/app/v1 里重新发明。原生客户端携带 Bearer token 调用同一套 /api/auth/* Better Auth 端点——见 原生认证

重要文件

文件作用
src/app/api/app/v1/路由处理器(只负责 HTTP 边界)
src/server/app/shared/Session 守卫、响应 Envelope、Request ID、日志、限流、幂等
src/server/app/{profile,todos,devices,push,subscriptions,uploads,account}/业务 Service
src/lib/db/schema/app.tstodosdevice_tokens
docs/openapi.yamlOpenAPI 3.1 契约——单一事实来源

架构

每个路由都是一层薄封装:authedRoute 构建请求上下文、强制校验 Session,并把 抛出的 AppError 映射为统一错误 Envelope。处理器把工作委托给 Service,后者 接收服务端确认的用户 ID

src/app/api/app/v1/todos/route.ts(示意)
export const GET = authedRoute(async (ctx) => {
  const items = await todosService.list(ctx.userId, filter);
  return ok({ items }, ctx);
});

Service 不导入 NextRequest/NextResponse,因此同一批函数同时支撑 Web 仪表盘和 App API。

API 约定

事项规则
鉴权每个请求携带 Authorization: Bearer <sessionToken>
JSON 命名camelCase
日期ISO 8601 UTC(2026-07-13T04:42:00.000Z
成功 Envelope{ "success": true, "data": … }
错误 Envelope{ "success": false, "error": { "code", "message", "requestId" } }
错误码稳定的 SCREAMING_SNAKE_CASE 枚举——按 code 分支,不要解析 message
Request ID可传 X-Request-Id,否则服务端生成;在响应 header 和错误体中回显
幂等非幂等写操作接受 Idempotency-Key header
版本策略v1 只做向后兼容变更;破坏性变更以 v2 并行发布

错误行为

HTTP错误码(示例)触发条件
400INVALID_REQUESTZod 校验失败、请求体格式错误
401UNAUTHENTICATEDSession 缺失、失效或已撤销
404*_NOT_FOUND资源不存在或属于其他用户
409SUBSCRIPTION_ALREADY_ACTIVE有效订阅期间重复购买
422UNPROCESSABLE语义校验失败(如不支持的上传类型)
429RATE_LIMITED触发限流
500INTERNAL未预期错误,不泄露内部细节

越权访问返回 404 而不是 403。 每个资源查询同时带资源 ID 和 Session 的 用户 ID 条件,因此 API 永远不会暴露其他用户的资源是否存在。

端点

方法路径用途
GET / PATCH/api/app/v1/profile读取 / 更新开放的资料字段
GET / POST/api/app/v1/todos列出 / 创建 todos
PATCH / DELETE/api/app/v1/todos/:id更新 / 删除单个 todo
POST/api/app/v1/todos/clear-completed删除已完成的 todos
PUT/api/app/v1/todos/reorder持久化完整排序
PUT / DELETE/api/app/v1/device-tokens[/:token]注册 / 删除 APNs token
POST/api/app/v1/notifications/test-push向当前用户的设备发送测试推送
GET/api/app/v1/subscription当前订阅快照 + isEntitled
POST/api/app/v1/subscription/portal支付平台托管的管理 URL(或 null
POST/api/app/v1/uploads申请 presigned 直传 URL
POST/api/app/v1/uploads/complete确认上传并获取可用 URL
DELETE/api/app/v1/account删除账户并级联清理所属数据

另有 PUT /api/app/v1/uploads/local,是本地存储 Provider 的仅开发环境 上传目标;生产环境禁用,不属于契约。

OpenAPI 契约

docs/openapi.yaml 描述了每个端点、Schema、枚举和错误响应,是客户端实现的 单一事实来源——可直接用于任何 OpenAPI 查看器或生成器,并在契约测试中校验 手写的客户端模型。

相关页面

本页目录