Files

8.9 KiB
Raw Permalink Blame History

name, description
name description
sync-backend-api Sync this Next.js frontend with backend API documentation

sync-backend-api

根据后端文档同步当前 Next.js / TypeScript 项目的前端代码。

使用场景

当后端 API 新增、删除、字段结构调整、响应语义变化时,使用本 skill 更新:

  • 网络层
  • schema / 不可变纯数据
  • repository
  • storage
  • XState store
  • UI 调用逻辑
  • mock 数据
  • 单元测试

当前项目结构

层级 路径 说明
API Path src/data/services/api/api_path.ts 统一维护接口路径
API Service src/data/services/api/*_api.ts 调用 httpClient 并解析响应
Schema Model src/data/schemas/<module>/*.ts Zod schema 与不可变纯数据类型,负责解析和归一化
Repository Interface src/data/repositories/interfaces/i*_repository.ts 仓库接口
Repository src/data/repositories/*_repository.ts 业务仓库实现
Storage src/data/storage/* 本地持久化
Store src/stores/<module>/* XState 状态机、actors、helpers、sync
UI src/app/** Next.js app router UI
Mock src/data/mock/<module>/** 模拟请求 / 响应数据
Tests **/__tests__/*.test.ts schema model、helper、machine transition 测试

后端文档来源

优先读取用户指定的文档或文件片段。

常见文档:

  • FRONTEND_VIP_CREDIT_API.md
  • PAYWALL_API.md
  • docs-change.md
  • docs/backend/openapi.json

如果用户没有指定文档,先搜索相关 API 路径或模块名,再选择最相关文档。

更新流程

1. 检查工作区

开始前必须执行:

git status --short

如果存在无关未提交改动:

  • 不要回退。
  • 不要覆盖。
  • 修改前先确认本次任务需要触碰的文件。
  • 提交时只暂存本次任务相关文件。

2. 阅读后端文档

确认:

  • API method
  • API path
  • 请求参数
  • 响应结构
  • 字段是否可空
  • 旧字段是否废弃
  • 是否需要认证 token
  • 是否影响本地持久化
  • 是否影响状态机或 UI

如果用户明确要求“不兼容旧字段,彻底更新”,不要保留旧字段 fallback。

否则 schema 应尽量防御性解析,例如:

z.string()
  .nullable()
  .transform((v) => v ?? "")
  .default("");

项目已有通用 helper

src / data / schemas / nullable - defaults.ts;

3. 更新 API Path

新增或修改:

src / data / services / api / api_path.ts;

示例:

static readonly userEntitlements = `${ApiPath._user}/entitlements`;

4. 更新 Schema Model

在对应模块下新增或修改:

src/data/schemas/<module>/*.ts

要求:

  • 使用 Zod。
  • 输出类型用 z.output<typeof Schema>
  • 输入类型用 z.input<typeof Schema>
  • 对后端可能返回 null 的字段做防御性处理。
  • 不要在 schema 中写 UI 逻辑。
  • Schema 输出必须是纯数据,不要定义 class 或工厂对象。
  • 对象 Schema 使用 .readonly();已知嵌套对象、数组和 Record 也必须逐层 readonly。
  • transform 应先完成归一化,再在最终输出应用 .readonly()
  • 容器默认值必须经过 Schema 解析;使用 prefault 或在最外层应用 readonly,不能让 .default() 返回未冻结对象。
  • 后端返回但前端既不读取、也不透传的字段,不要加入 Schema。
  • 仅用于请求序列化、响应兼容或内部透传的字段直接保留在 Schema 中。
  • 统一使用 XxxSchema.parse() 构造请求与解析响应,不增加 from()fromJson()toJson() 包装。
  • 不要再创建独立的数据传输对象层。

示例:

export const XxxResponseSchema = z
  .object({
    id: z.string(),
    tags: z
      .array(z.string())
      .default(() => [])
      .readonly(),
    count: z.number().default(0),
  })
  .readonly();

export type XxxResponseInput = z.input<typeof XxxResponseSchema>;
export type XxxResponseData = z.output<typeof XxxResponseSchema>;
export type XxxResponse = XxxResponseData;

5. 更新 API Service

修改:

src/data/services/api/<module>_api.ts

统一调用方式:

const env = await httpClient<ApiEnvelope<unknown>>(ApiPath.xxx, {
  method: "POST",
  body: request,
});
return XxxResponseSchema.parse(unwrap(env));

注意:

  • 不要直接返回 unknown
  • 不要在 API 层处理 UI 行为。
  • multipart 仍使用 FormData

6. 更新 Repository

修改:

src/data/repositories/interfaces/i<module>_repository.ts
src/data/repositories/<module>_repository.ts

Repository 返回项目通用 Result<T>

async getXxx(): Promise<Result<XxxResponse>> {
  return Result.wrap(() => this.api.getXxx());
}

Repository 可以做轻量编排,例如:

  • request schema 解析
  • 多接口组合
  • 本地 storage 同步
  • 本地模型与接口模型转换

不要把 React / UI 逻辑放进 repository。

7. 更新 Storage

如果接口影响本地持久化,修改:

src/data/storage/storage_keys.ts
src/data/storage/<module>/*

原则:

  • 只持久化业务需要的最小字段。
  • 不要为了接口响应完整而盲目存完整 payload。
  • storage 使用 SpAsyncUtil 和 Zod schema 校验。

示例:

getEntitlementSnapshot(): Promise<Result<UserEntitlementSnapshotData | null>> {
  return SpAsyncUtil.getJson(
    StorageKeys.userEntitlementSnapshot,
    UserEntitlementSnapshotSchema,
  );
}

8. 更新 Store / 状态机

如果接口影响状态流,修改:

src/stores/<module>/<module>-state.ts
src/stores/<module>/<module>-events.ts
src/stores/<module>/<module>-machine.ts
src/stores/<module>/<module>-machine.actors.ts
src/stores/<module>/<module>-machine.helpers.ts
src/stores/<module>/<module>-context.tsx
src/stores/<module>/*-sync.tsx

项目约定:

  • API 调用放在 actors.ts
  • 纯函数转换放在 helpers.ts
  • 跨 store 监听放在 *-sync.tsx
  • 页面组件不承担全局监听职责。
  • 状态机事件应表达业务事实,而不是 UI 操作细节。

9. 更新 UI

只有在接口变更影响展示或用户明确要求时,才修改:

src/app/**

原则:

  • UI 只负责渲染和派发事件。
  • 不在页面组件里直接写复杂业务编排。
  • 全局监听逻辑优先放到 stores/*/*-sync.tsx

10. 更新桶文件

如果新增 schema model 或 component,更新对应 index.ts

src/data/schemas/<module>/index.ts
src/app/**/components/index.ts

如果项目使用 barrelsby 生成,不要手动破坏现有导出格式。

11. 更新 Mock 数据

如果新增或调整数据结构,按一个结构一个文件的原则放到:

src/data/mock/<module>/requests
src/data/mock/<module>/responses
src/data/mock/<module>/websocket-events

不要把多个结构塞进一个大 JSON。

12. 更新测试

优先补充:

  • schema model 解析与序列化测试
  • helper 纯函数测试
  • XState transition 测试
  • repository 编排测试(如已有测试基础)

常见位置:

src/data/schemas/<module>/__tests__/*.test.ts
src/stores/<module>/__tests__/*.test.ts

13. 验证

至少运行:

pnpm exec tsc --noEmit
pnpm exec eslint <changed files>
pnpm exec vitest run <related tests>

如果没有相关测试,需要说明未覆盖的风险。

常见任务模板

新增接口

  1. 文档确认 path / method / payload。
  2. api_path.ts 新增路径。
  3. 在同一文件新增 readonly schema 与输入/输出类型。
  4. API service 新增方法。
  5. repository interface + implementation 新增方法。
  6. 如需要,接入 store actor / event。
  7. 补 mock 和测试。
  8. 运行验证。

修改响应字段

  1. 更新 schema。
  2. 更新同文件输入/输出类型及相关 parse 调用。
  3. 更新 mapper/helper。
  4. 更新 UI 或状态机依赖字段。
  5. 更新 mock 和测试。
  6. 运行验证。

删除旧接口

  1. 搜索所有引用。
  2. 删除 API path / API method / repository method。
  3. 删除 schema model、mock 和测试。
  4. 更新状态机和 UI 调用。
  5. 运行 rg 确认无残留引用。
  6. 运行验证。