mcpField
D
data-client-schema

data-client-schema

github.com/reactive开发2.0k38 次安装今天更新
代码仓库

关于 data-client-schema

data-client-schema 是指导开发者为 API JSON 定义组合式数据模式的技能,适用于使用 @data-client 的应用。它涵盖实体、联合类型、集合和查询等结构,以及规范化缓存的建模方式。

主要功能

  1. 01定义实体与联合模式
  2. 02组合对象、列表和映射
  3. 03建模可变集合
  4. 04创建记忆化查询
  5. 05处理嵌套与部分实体

适用场景

  1. 01描述 API 返回的复杂 JSON
  2. 02规范化关联数据并管理缓存
  3. 03为多态数据定义联合模式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/reactive/data-client --skill data-client-schema

安装后在 Claude Code 中输入/data-client-schema确认已加载。

详细介绍

1. 定义架构

定义 架构,以表示端点返回的 JSON。组合这些架构来表示预期的数据。

对象
列表
映射
  • new Collection(Values(Schema)) - 可变/可扩展映射
  • new Values(Schema) - 不可变映射
依赖 Lens 的实体字段
派生/选择器模式
  • new Query(Queryable) - 经过记忆化的程序化选择器

    const queryRemainingTodos = new Query(
      TodoResource.getList.schema,
      entries => entries.filter(todo => !todo.completed).length,
    );
    
    const groupTodoByUser = new Query(
      TodoResource.getList.schema,
      todos => Object.groupBy(todos, todo => todo.userId),
    );
    

    使用数据模型(例如 src/resources/)定义 Query 转换,而不要在自定义 hook 中封装 useSuspense/useQuery;这种做法会隐藏数据依赖,并使数据逻辑与视图代码耦合。


2. Entity 最佳实践

  • 每个 Entity 子类都要为所有非可选序列化字段定义默认值。
  • 仅当主键 ≠ id 时才覆盖 pk()。
  • pk() 的返回类型是 number | string | undefined
  • 覆盖 Entity.process(value, parent, key, args),根据 args/url 插入字段
  • static schema(可选),用于嵌套架构或反序列化函数
    • 设计 API 时,优先嵌套实体

3. Entity 生命周期方法

  • 规范化(JSON 响应 → 缓存):处理 POJO;输出为 JSON 可序列化的普通数据,存储在规范化缓存中。顺序:process() → pk() → validate() → 遍历嵌套架构(递归处理 schema 字段)→ 如果已存在:mergeWithStore(),它会调用 shouldUpdate(),并可能调用 shouldReorder() + merge();元数据通过 mergeMetaWithStore() 处理。
  • 反规范化(缓存 → 组件):通过 fromJS() 创建 Entity 类实例,还原原型链,使 getter、方法和 schema 处理正常工作。顺序:createIfValid() → validate() → fromJS() → 取消遍历嵌套架构(递归处理 schema 字段)。

4. 联合类型(多态架构)

要定义多态资源(例如事件),请使用 Union 和判别字段。

import { Union } from '@data-client/rest'; // also available from @data-client/endpoint

export abstract class Event extends Entity {
  type: EventType = 'Issue';    // discriminator field is shared
  /* ... */
}
export class PullRequestEvent extends Event { /* ... */ }
export class IssuesEvent extends Event { /* ... */ }

export const EventResource = resource({
  path: '/users/:login/events/public/:id',
  schema: new Union(
    {
      PullRequestEvent,
      IssuesEvent,
      // ...other event types...
    },
    'type', // discriminator field
  ),
});

5. 集合(可变列表与映射)

Collections 会包装 Array 或 Values 架构,以支持变更(添加/移除/移动)。

pk 路由

当嵌套在 Entity 中且可用时,pk() 会使用 nestKey(parent, key);否则使用 argsKey(...args),然后序列化结果。未提供选项时,默认使用 argsKey: params => ({ ...params }),将所有端点参数作为集合键。

  • argsKey — 根据端点参数派生 pk(默认)
  • nestKey — 根据父实体派生 pk,用于嵌套的共享状态集合

在同一个 Collection 上同时定义两者,即可在顶层和嵌套场景复用同一个定义。当 argsKey(args) 和 nestKey(parent) 生成相同的对象结构时,顶层获取与嵌套读取会解析为同一个(引用相等的)数组/映射——对任一方执行 push/unshift/assign/move/remove 都会同时更新两者:

const userTodos = new Collection([Todo], {
  argsKey: ({ userId }: { userId?: string }) => ({ userId }),
  nestKey: (parent: User) => ({ userId: parent.id }),
});
nonFilterArgumentKeys

默认情况下,createCollectionFilter 使用 nonFilterArgumentKeys(默认值:以 'order' 开头的键)在匹配集合时排除非筛选参数。这会影响哪些现有集合会接收由 push/unshift/assign/move 添加的新项。

可将其覆盖为函数、RegExp 或 string[]:

new Collection([Todo], { nonFilterArgumentKeys: /orderBy|sortDir/ })
Extenders

这些方法均可通过 ctrl.set() 使用(仅限本地),也可通过 RestEndpoint extenders 使用(网络)。

方法类型描述
pushArrayEntity
unshiftArrayEntity
assignValues将条目合并到映射中
removeBoth按值从匹配的集合中移除项
moveBoth从与现有状态匹配的集合中移除,再添加到与新状态匹配的集合中
addWith(merge, filter?)Both自定义创建模式(内部由 push/unshift/assign 使用)
moveWith(merge)Both自定义移动模式(控制插入顺序,例如使用 unshift 合并以添加到开头)

6. 补充端点(丰富现有实体)

当端点为缓存中已有的实体返回部分数据或形状不同的数据时 (例如元数据端点、统计端点、延迟加载扩展端点), 请将同一个 Entity 用作模式,不要创建包装实体。

请参阅 partial-entities,了解相关模式和示例。


7. 最佳实践与说明

  • 始终为每个资源/实体/集合设置 schema,以便进行规范化
  • 通过定义适当的模式,对深层嵌套或关联数据进行规范化
  • 使用 Entity.schema 进行客户端连接
  • 使用 rest/endpoint/graphql 中的 Denormalize<> 类型,而不是 InstanceType<>。这样可以处理所有模式(例如 Unions),而不仅仅是 Entity。

8. 应避免的常见错误

  • 规范化缓存存储的是可序列化为纯 JSON 的对象(POJO),而不是类实例。
  • 别忘了使用 fromJS() 或为类字段指定默认属性——单独的 TS 字段类型不会生成运行时默认值,因此模式推断会失效
  • 手动合并或“丰富”数据;应改用 Entity.schema 进行客户端连接

参考

如需查看详细的 API 文档,请参阅 references 目录:

评论

支持 Markdown