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
モデルは、サーバーの 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。null 許容にするには ? を使用します。
必須フィールド
class: Person
fields:
name: String
nickname: String?, required
データベーステーブル
データベーステーブルと ORM を使用するには、table を追加します。
class: Company
table: company
fields:
name: String
foundedDate: DateTime?
デフォルト値
default=は、Dart モデルとデータベースの列の両方に値を設定します。defaultModel=は、Dart モデルのみに値を設定します(Dart でオブジェクトを作成するときに使用されます)。defaultPersist=は、データベースの列のみに値を設定します(idでない限り、Dart ではフィールドを null 許容にする必要があります)。
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 フィールドはデフォルト値が serial の int です。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 がわかります。id: int?, defaultPersist=serial— デフォルトの動作を明示した形式です。
カスタム ID 型を持つモデルとのリレーションでは、外部キーのフィールドにも同じ型を使用します。
スコープ
- サーバー専用クラス:
serverOnly: true - フィールド単位:
scope=serverOnly、scope=none(デフォルトはall) - 永続化しないフィールド:
!persist(DB に保存されません) - JSON キーの別名:
jsonKey=display_name
イミュータブルクラス
immutable: true — フィールドが final になり、==、hashCode、copyWith が利用できます。
継承
extends: ParentClass— 子クラスは親クラスのフィールドを継承します。階層内でtableを持つクラスは 1 つだけにします。sealed: true— すべてのサブタイプを網羅する抽象 sealed 階層を定義します。sealed クラスにはtableを指定しません。
親が serverOnly の場合、子も serverOnly にする必要があります。子クラスで親のフィールドを再定義することはできません。
列挙型
enum: Status
values:
- pending
- active
- completed
デフォルトのシリアライズ方式は byName です。インデックスを使用するには、serialized: byIndex を設定します。
例外
シリアライズ可能な例外を定義するには、class: の代わりに exception: を使用します。フィールドの型はクラスと同じで、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])
リレーション
1対1(一意インデックス付きの外部キー):
- ID フィールド:
addressId: int, relation(parent=address)+addressIdの一意インデックス - オブジェクトフィールド:
address: Address?, relation(addressIdを生成します) - オプション:NULL 許容の外部キーには
relation(optional)、外部キー名をカスタムするにはrelation(field=customId)を使います - 双方向:両側に同じ
relation(name=...)を指定し、外部キー側にfkを指定します(外部キー名をカスタムする場合はfield=を指定します)
1対多:
- 「1」側:
employees: List<Employee>?, relation - 「多」側:
companyId: int, relation(parent=company)(一意インデックスはありません) - 双方向:両側に
relation(name=company_employees)を指定します
多対多:2つのリレーションフィールドを持つ結合テーブルモデルを使用します。
参照アクション: relation(onDelete=Cascade) と relation(onUpdate=...) は、SQL の外部キーアクション(Cascade、SetNull、SetDefault、Restrict、NoAction)に対応します。SetNull には、NULL 許容の外部キー(NULL 許容のオブジェクトリレーションフィールド、または relation(optional))が必要です。制約チェックを明示的に遅延できるようにするには deferrable を追加し、デフォルトでコミットまで遅延させるには deferred を追加します。Restrict アクションは、制約が遅延可能な場合でも常に即時にチェックされます。チェックを遅延可能にする必要がある場合は、NoAction を使用してください。
クエリ:先読みには include を、リストリレーションには where/orderBy/limit/offset を指定した includeList を使用します。リレーションの管理には attach/detach を使用します。
後方互換性
後方互換性を維持するため、クライアントが使用するシリアライズ済みクラスのフィールドを変更または削除しないでください。新しいフィールドを追加する場合は、NULL 許容にするかデフォルト値を設定してください。そうすることで、フィールドを送信しない古いクライアントも引き続き動作します。
リファレンスファイル
references/client-side-database.md—database:キーワードと、クライアントでのテーブル生成について説明します。references/custom-serialization.md—extraClassesを介して、手書きのクラスまたは Freezed クラスをモデル型として使用する方法を説明します。