| name | manage-api |
| description | Add and manage user-facing API endpoints in the Cat project using the CatApiSpec DSL. Use for the `api` plan layer, frontend/public APIs, schemas, generated request modules, and routes. For `/api/admin/...` endpoints use `manage-api-admin`. |
Manage API
Workflow Overview
本 skill 只负责面向用户/前端的 API。管理端 API 属于 api_admin 层,使用 manage-api-admin。
添加前端 API 分为 6 步:DSL 定义 -> 注册模块/Tag -> 生成产物 -> 添加路由并选择 pipeline -> 实现控制器/JSON -> 更新 PRD。
Task Progress:
- [ ] Step 1: DSL 定义(api + schema)
- [ ] Step 2: 注册模块与前端 Tag(如果是新 context)
- [ ] Step 3: 生成产物(mix cat.gen.spec)
- [ ] Step 4: 添加路由并选择正确 pipeline
- [ ] Step 5: 实现控制器 + JSON 渲染
- [ ] Step 6: 更新 docs/prds/{domain}/features/{feature}.md 的 `## api`
Step 1: DSL 定义
在 lib/cat_api_spec/<context>.ex 中定义 api 和 schema。
DSL 语法详见 dsl-reference.md。
列表响应 schema 约定:
- 标准
{items, pagination} 结构优先使用 paginated_schema
- 标准
{items, cursor_pagination} 结构优先使用 cursor_paginated_schema
- 如果分页 schema 还需要
filters、filter_groups 等附加字段,继续在宏后追加 do ... end
- 只有在同一个 schema 同时包含
pagination 和 cursor_pagination,或字段布局明显不适合该抽象时,才继续手写 schema
路径约定
| 类型 | 路径格式 | 示例 |
|---|
| 前端 API | /api/<context>/<action> | /api/catalog/get_class |
Admin API 使用 /api/admin/<context>/<action>,不要在本 skill 中实现;转到 manage-api-admin。
Schema 命名约定
| 类型 | 命名模式 | 示例 |
|---|
| 请求表单 | <Action>Form | CreateClassForm |
| 响应实体 | 实体名称 | Class, Property |
| 响应详情 | <Entity>Detail | ClassDetail |
| 列表响应 | List<Entity>Resp | ListClassesResp(data 为 {items, pagination, filters}) |
| 游标分页 | <Entity>ListResp | FollowListResp(data 为 {items, cursor_pagination, filters}) |
| 空成功 | "Null" | response(200, "Null")(data 为 null) |
API JSON Response Contract
所有 200 响应均包装在 {success: true, data: <schema>} 信封中。response(200, "PostDetail") 表示 data 为 PostDetail 对象;response(200, "Null") 表示 data 为 null。
资源 id 在 API/JSON 中使用 string 类型。
分页响应示例:
paginated_schema "ListClassesResp", "ClassDetail",
items: [description: "类型列表"] do
field :filters, :object, description: "筛选条件", nullable: true
end
cursor_paginated_schema "FollowListResp", "FollowUser",
items: [description: "关注列表"]
示例:前端 API
defmodule CatApiSpec.Catalog do
use CatApiSpec.Schema
use CatApiSpec.Api
api "/api/catalog/get_class" do
method(:get)
operation(tags: ["Catalog"], summary: "查看类型", description: "查看类型")
parameter(:id, :string, :query, description: "类型 ID", required: true)
response(200, "ClassDetail")
end
end
前端 API 的 tags 使用 <Context> 格式(如 "Catalog"),生成的 Request 模块名称为 CatWeb.CatalogRequest。
Step 2: 注册模块与前端 Tag
如果是新的 context 模块,需在 lib/cat_api_spec.ex 中注册:
- 添加 alias:
alias CatApiSpec.NewContext
- 加入
@all_modules 列表
- 添加前端 Tag
lib/cat_api_spec.ex 当前有 build_spec(:all)、build_spec(:api)、build_spec(:admin) 三套独立的 tags 列表。前端 API 新增 Tag 时必须同步:
| API 类型 | build_spec(:all) | build_spec(:api) | build_spec(:admin) |
|---|
前端 API Tag(如 NewContext) | 要加 | 要加 | 不加 |
Admin API Tag(如 AdminNewContext) | 转 manage-api-admin | 转 manage-api-admin | 转 manage-api-admin |
示例:
alias CatApiSpec.{Accounts, Catalog, Misc, Subjects, Rankings, NewContext}
@all_modules [Accounts, Catalog, Misc, Subjects, Rankings, NewContext]
def build_spec(:all) do
%OpenApi{
tags: [
# ...existing tags...
%Tag{name: "NewContext", description: "描述"}
]
}
end
def build_spec(:api) do
%OpenApi{
tags: [
# ...existing tags...
%Tag{name: "NewContext", description: "描述"}
]
}
end
已有模块中添加 API 不需要添加 alias 或修改 @all_modules,但新增 Tag 仍要按上表同步。
Step 3: 生成产物
mix cat.gen.spec
生成三类文件:
lib/cat_web/generated/<tag>_request.ex — Request 验证模块
lib/cat_web/schemas/<name>.gen.ex — Schema Struct
openapi/openapi.yaml — OpenAPI 文档
Step 4: 添加路由并选择 pipeline
在 lib/cat_web/router.ex 中添加路由。不要默认放进 :api,先按访问控制选择 pipeline。
当前 API 相关 pipeline:
| pipeline | 用途 | 典型场景 |
|---|
:public_api | 无需登录;会尝试读取当前用户 | 公开只读、登录注册、API docs;如 get_subject、list_classes |
:api | 需要登录且二次验证已完成 | 普通用户写操作、个人数据读取 |
:partial_api | 需要登录但不要求二次验证完成 | sign_out 等少数流程 |
:second_factor_api | 已登录且处于待 TOTP 验证状态 | verify_sign_in_totp |
:security_api | 需要安全设置访问权限 | TOTP 初始化/确认、恢复码等安全设置 |
:admin_api | 管理员接口 | 不在本 skill 处理,使用 manage-api-admin |
示例:
scope "/api", CatWeb.API do
pipe_through [:public_api]
get "/catalog/list_classes", CatalogController, :list_classes
end
scope "/api", CatWeb.API do
pipe_through [:api]
get "/catalog/get_class", CatalogController, :get_class
post "/catalog/create_class", CatalogController, :create_class
end
HTTP 方法规则:读操作用 get,写操作用 post。
Step 5: 实现控制器 + JSON 渲染
控制器
路径:lib/cat_web/controllers/api/<context>_controller.ex
模块:CatWeb.API.<Context>Controller
defmodule CatWeb.API.CatalogController do
use CatWeb, :api_controller
alias CatWeb.CatalogRequest
alias Cat.Catalog
alias Cat.Catalog.Class
def create_class(conn, _params) do
with {:ok, conn} <- cast_and_validate(conn, CatalogRequest.operation(:post, :create_class)),
{:ok, %Class{} = class} <- Catalog.create_class(conn.body_params) do
render(conn, :class, class: class)
end
end
end
cast_and_validate/2 后,conn.params 和 conn.body_params 会被替换为 atom 键;控制器读取参数时优先使用 params[:key] / conn.params[:key]。
JSON 渲染模块
路径:lib/cat_web/controllers/api/<context>_json.ex
模块:CatWeb.API.<Context>JSON
JSON 模块使用 CatWeb.API.ResponseHelper 构建符合 API JSON Response Contract 的响应:
defmodule CatWeb.API.CatalogJSON do
import CatWeb.API.ResponseHelper
alias CatWeb.Schemas.ClassDetail
def class(%{class: class, properties: properties}) do
success(%ClassDetail{
id: class.id,
label: class.label,
properties: Enum.map(properties, &build_property/1)
})
end
def list_classes(%{classes: classes, metadata: metadata}) do
paginated(Enum.map(classes, &build_class/1), metadata)
end
def deleted_reply(_params), do: ok()
end
控制器模式要点
use CatWeb, :api_controller 自动设置 action_fallback
cast_and_validate/2 做请求参数校验与转换
with 链式调用处理业务逻辑
render/3 调用同名 JSON 模块
- 前端控制器引用
<Context>Request
Step 6: PRD 文档
在 docs/prds/{domain}/features/{feature}.md 的 ## api 小节中记录前端 API 设计。PRD 已按 domain + feature 分层,不再创建或更新 docs/prds/{domain}/api.md 这种 aspect 文件。
选择 feature 的规则:
- 先阅读
docs/prds/{domain}/overview.md 的 Feature 列表和 features/ 下已有文档。
- 如果该 API 服务某个已有业务能力,更新对应 feature 的
## api。
- 如果 API 引入新的可复制业务能力包,先按
manage-prd 的规则创建 / 更新 feature,并同步 overview.md 的 Feature 列表。
- 如果只是补充字段、筛选项或分页语义,通常并入现有 feature,不单独创建 feature。
- 如果 API 变更影响共享实体、字段或状态,必要时同步更新 domain
data_model.md,并让 feature ## data_model 保持自包含。
管理端 API 文档写入对应 feature 的 ## api_admin,由 manage-api-admin 负责。
文件对照表
| 文件 | 前端 API |
|---|
| DSL 定义 | lib/cat_api_spec/<ctx>.ex |
| Request 模块 | generated/<ctx>_request.ex |
| 路由 | scope "/api" + 正确 pipeline |
| 控制器 | controllers/api/<ctx>_controller.ex |
| JSON 渲染 | controllers/api/<ctx>_json.ex |
| PRD | docs/prds/{domain}/features/{feature}.md 的 ## api |