关于 serverpod-models
serverpod-models 是 Serverpod 项目模型定义的技能,面向编写和维护数据模型的开发者。它说明如何用 .spy.yaml 配置生成 Dart 类、数据库表及序列化行为。
主要功能
- 01定义模型、枚举和异常
- 02配置字段类型与默认值
- 03生成数据库表和 ORM 关系
- 04设置索引与关联规则
- 05管理序列化兼容性
适用场景
- 01为服务端和客户端定义共享模型
- 02为模型配置数据库持久化
- 03设计关联、索引和主键
- 04维护客户端兼容的数据结构
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ serverpod/ serverpod --skill serverpod-models安装后在 Claude Code 中输入/serverpod-models确认已加载。
附带文件references/client-side-database.mdreferences/custom-serialization.md
详细介绍
Serverpod Models
模型定义在 server 的 lib/ 下任意位置的 .spy.yaml 文件中。它们会为服务端和客户端生成 Dart 类,也可以选择生成数据库表。
每次更改模型后,都要确保代码已生成(Serverpod runner 运行时会自动生成,也可以手动运行 serverpod generate)。如果更改了带有 table 的模型,必须按照迁移工作流更新数据库架构。
基本类
class: Company
fields:
name: String
foundedDate: DateTime?
employees: List<Employee>
字段类型:bool、int、double、String、Duration、DateTime、ByteData、UuidValue、Uri、BigInt、生成的类/枚举/异常、List<T>、Map<K,V>、Set<T>、Record。使用 ? 表示可空。
必填字段
class: Person
fields:
name: String
nickname: String?, required
数据库表
添加 table 以创建数据库表和 ORM:
class: Company
table: company
fields:
name: String
foundedDate: DateTime?
默认值
default=会同时设置 Dart 模型和数据库列的值。defaultModel=仅在 Dart 模型中设置(在 Dart 中创建对象时使用)。defaultPersist=仅在数据库列中设置(除非字段为id,否则该字段在 Dart 中必须可空)。
class: Post
table: post
fields:
title: String, default='Untitled'
createdAt: DateTime, default=now
publishedAt: DateTime?, defaultPersist=now
externalId: UuidValue, default=random_v7
views: int, default=0
isPublic: bool, default=false
支持 now(DateTime)、random/random_v7(UuidValue)和带引号的字面值。具有默认值的字段无需传入构造函数。
自定义 id 类型
除非显式声明,id 字段的类型为 int,默认值为 serial。显式声明即可使用 UUID 主键:
class: Company
table: company
fields:
id: UuidValue?, defaultPersist=random_v7 # database generates the id
name: String
defaultPersist=random_v7— 数据库在插入时生成 id(v4 使用random)。defaultModel=random_v7— Dart 在创建对象时生成 id,因此插入前即可获知。id: int?, defaultPersist=serial— 默认行为的显式写法。
与自定义 id 类型模型的关系使用相同类型的外键字段。
作用范围
- 仅限服务端的类:
serverOnly: true - 单个字段:
scope=serverOnly、scope=none(默认值为all) - 不持久化的字段:
!persist(不存储在数据库中) - JSON 键别名:
jsonKey=display_name
不可变类
immutable: true — 字段为 final,并生成 ==、hashCode 和 copyWith。
继承
extends: ParentClass— 子类继承父类字段。继承层级中只能有一个类带有table。sealed: true— 用于穷尽子类型的抽象密封层级。密封类不能带有table。
如果父类是 serverOnly,子类也必须是。子类不能重新定义父类字段。
枚举
enum: Status
values:
- pending
- active
- completed
默认序列化方式:byName。设置 serialized: byIndex 可使用索引。
异常
使用 exception: 代替 class: 来声明可序列化异常。字段类型与类相同;支持 default 和 defaultModel。未捕获的异常会在客户端变成通用 500 错误;只有可序列化异常才会发送其数据。
exception: MyException
fields:
message: String
errorType: MyEnum
在服务端抛出,在客户端捕获:
// Server
throw MyException(message: 'Failed', errorType: MyEnum.thingyError);
// Client
try {
await client.example.doThingy();
} on MyException catch (e) {
print(e.message);
}
可序列化异常也可以通过流发送(支持双向发送;发送后流会关闭)。不要在异常字段中放入敏感数据,因为这些数据会发送给客户端。
索引
indexes:
company_name_idx:
fields: name
unique: true
nulls_distinct: false # PostgreSQL only, treats NULLs as equal
字段级的 unique 会自动生成 btree 唯一索引:
fields:
tenantId: int
category: String
# single-column unique index
email: String, unique
# composite unique index on (category, value)
value: String, unique(per=category)
# composite unique index on (category, tenantId, value)
amount: int, unique(per=[category, tenantId])
关系
一对一(带唯一索引的外键):
- ID 字段:
addressId: int, relation(parent=address)+ 在addressId上建立唯一索引 - 对象字段:
address: Address?, relation(会生成addressId) - 可选:使用
relation(optional)设置可空外键;使用relation(field=customId)自定义外键名 - 双向关系:两侧使用相同的
relation(name=...);在外键侧使用fk(自定义外键名时使用field=)
一对多:
- “一”侧:
employees: List<Employee>?, relation - “多”侧:
companyId: int, relation(parent=company)(无唯一索引) - 双向关系:两侧使用
relation(name=company_employees)
多对多: 使用包含两个关系字段的连接表模型。
引用操作: relation(onDelete=Cascade) 和 relation(onUpdate=...) 会映射到 SQL 外键操作(Cascade、SetNull、SetDefault、Restrict、NoAction)。SetNull 要求外键可空(使用可空对象关系字段或 relation(optional))。添加 deferrable 可显式延迟约束检查,添加 deferred 可默认将检查延迟到提交时。Restrict 操作始终会立即检查,即使约束已延迟;需要延迟检查时请使用 NoAction。
查询:使用 include 进行预加载;对于列表关系,使用带有 where/orderBy/limit/offset 的 includeList。使用 attach/detach 管理关系。
向后兼容性
为保持向后兼容,请勿更改或删除客户端使用的序列化类中的字段。只有在新字段可空或具有默认值时才添加,这样较旧客户端未发送该字段时也能正常工作。
参考文件
references/client-side-database.md—database:关键字,用于在客户端生成表。references/custom-serialization.md— 通过extraClasses使用手写类或 Freezed 类作为模型类型。