zenstack-schema-modeling について
zenstack-schema-modelingは、ZenStackのZModelでデータベース構造を定義する開発者向けのスキルです。ZModelはPrisma Schemaを拡張し、ミックスイン、多態モデル、型付きJSON、計算フィールドを扱えます。
主な機能
- 01モデル、フィールド、列挙型の定義
- 02一対一・多対多リレーションのモデリング
- 03カスタム型とフィールドミックスイン
- 04多態モデルの継承
- 05型付きJSONフィールド
- 06計算フィールドとスキーマ検証
活用シーン
- 01アプリケーションのデータモデルと関連を定義
- 02モデル間で共通フィールドを再利用
- 03多態データやJSONデータをモデリング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ zenstackhq/ skills --skill zenstack-schema-modelingインストール後、Claude Code で/zenstack-schema-modelingを入力して読み込まれたか確認します。
詳細
ZenStack — スキーマモデリング(ZModel)
ZModelはZenStackのスキーマ言語で、Prismaのスキーマを拡張したものです。スキーマはデフォルトで
zenstack/schema.zmodel にあります。アクセス制御ポリシーやバリデーションには zenstack-access-control、
クエリには zenstack-querying を使用してください。
スキーマを編集した後:
zen check # only validate the schema for syntax/semantic errors (use --schema <file> for a non-default path)
zen generate # regenerate the database client — also validates the schema as it compiles
スキーマが有効かどうかだけを確認する場合は zen check を実行します。クライアントを再生成する場合は
zen generate を実行します(スキーマ変更後の通常の手順です)。
文字列にはシングルクォートとダブルクォートの両方を使用できます。有効なPrismaスキーマはすべて有効なZModelです。 そのため、既存のPrismaの知識をそのまま活用できます。以下のセクションでは、基本事項とZModel独自の拡張について説明します。
データソース
スキーマごとに datasource ブロックは1つだけ指定できます。ORMのランタイムは url を使用しません(接続は
ドライバーが担います)。マイグレーションエンジンでは使用します。
datasource db {
provider = 'postgresql' // 'postgresql' | 'mysql' | 'sqlite'
url = env('DATABASE_URL') // or 'file:./dev.db' for SQLite
}
ZModelには
generatorブロックがありません。コード生成はzen generateによって行われます。オプションの動作はpluginブロック(例:plugin policy、plugin prisma)で設定します。
モデルとフィールド
model User {
id Int @id @default(autoincrement())
email String @unique
name String? // optional
role Role @default(USER)
tags String[] // list
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
スカラー型:String、Boolean、Int、BigInt、Float、Decimal、DateTime、Json、
Bytes、Unsupported("...")。
修飾子:? はオプション、[] はリストを表します。フィールドにオプションとリストの両方を指定することはできません。
フィールド属性(@)
@id、@unique、@default(...)、@updatedAt、@map('col')、@relation(...)、@db.*(ネイティブ
型。例:@db.VarChar(64))、@json(型付きJSONフィールド)、@computed(仮想フィールド)。
モデル属性(@@)
@@id([a, b])(複合主キー)、@@unique([a, b])、@@index([a, b])、@@map('table')、
@@delegate(field)(ポリモーフィズム)、および @@allow / @@deny / @@validate(詳細は
zenstack-access-control を参照)。
model City {
country String
name String
@@id([country, name]) // composite primary key
@@map('cities')
}
モデルには識別子が必要です。@id フィールド、@@id([...])、または、それらがない場合は @unique
フィールドか @@unique([...]) を指定してください。
デフォルト値関数
autoincrement()、now()、cuid()、uuid() / uuid(4)、ulid()、nanoid()、
dbgenerated("...") のほか、リテラル値と列挙型の値を使用できます。
v3.1.0以降、文字列ID生成関数では %s を含む format を指定できます。
id String @id @default(uuid(4, "user_%s")) // -> "user_<uuid>"
列挙型
enum Role {
USER
ADMIN
}
列挙型の値の名前はグローバルであり、重複してはなりません。
リレーション
所有側には外部キー(@relation(fields: [...], references: [...]))を指定します。
1対1 — 外部キーは @unique であり、非所有側はオプションです。
model User {
id Int @id
profile Profile?
}
model Profile {
id Int @id
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
1対多 — 非所有側にリストを指定します。
model User {
id Int @id
posts Post[]
}
model Post {
id Int @id
author User @relation(fields: [authorId], references: [id])
authorId Int
}
多対多(暗黙) — 両側にリストを指定すると、マイグレーションエンジンが結合テーブルを作成します。
model User { id Int @id posts Post[] }
model Post { id Int @id editors User[] }
多対多(明示) — 追加フィールドが必要な場合は、結合テーブルを独自にモデル化します。
model UserPost {
user User @relation(fields: [userId], references: [id])
userId Int
post Post @relation(fields: [postId], references: [id])
postId Int
@@id([userId, postId])
}
名前付きリレーションは、同じ2つのモデル間に複数のリレーションがある場合に区別するためのものです(両端に
同じ名前を付けます):@relation('UserPosts', ...)。
自己リレーションでは名前付きリレーションを使用します。たとえば、1対多のマネージャー/部下のツリーです。
model Employee {
id Int @id
managerId Int?
manager Employee? @relation('Mgmt', fields: [managerId], references: [id])
subordinates Employee[] @relation('Mgmt')
}
参照アクションは所有側で指定します:onDelete / onUpdate に Cascade、Restrict、
NoAction、SetNull、SetDefault を指定します。
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
カスタム型とミックスイン
type 宣言はテーブルを作成しません。型付きJSON(以下を参照)または再利用可能なフィールドのミックスインとして
使用し、with で適用します。
type BaseFields {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model User with BaseFields {
email String @unique
}
model Post with BaseFields {
title String
}
ミックスインのフィールドはモデルにインライン展開されます。名前が重複しなければ、複数のミックスインを使用できます。 これは、モデル間でフィールドを共有するZModelの仕組みです(抽象基底モデルの代わりに使用してください)。
型にリレーションフィールドを含めることもできます(v3.6.0以降)。ただし、その場合はミックスインとしてのみ使用でき、 JSONフィールドとしては使用できません。
type AuditMixin {
createdBy User @relation('CreatedBy', fields: [createdById], references: [id])
createdById String
}
model Post with AuditMixin { title String }
ポリモーフィズム — @@delegate
複数テーブル継承では、判別フィールドと @@delegate を持つ基底モデル、および extends を使用する具象モデルを
定義します。具象モデルと基底モデルの行は同じIDを共有します。作成するのは具象モデルであり、基底モデルを直接作成することはありません。
model Content {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
type String // discriminator
@@delegate(type)
}
model Post extends Content {
content String
}
model Image extends Content {
data Bytes
}
保存される判別値は @@delegateMap でカスタマイズできます(v3.7.1以降)。列挙型の判別子にも使用できます。
model Video extends Content {
url String
@@delegateMap("video")
}
強い型付けの JSON
type を定義してフィールドの型として使用し、そのフィールドに @json を付けます。データベース上の列は通常の JSON ですが、ZenStack は書き込みを検証し、型付けされた値を返します。
type Address {
street String
city String
zip Int
}
model User {
id Int @id
address Address @json
}
計算フィールド
@computed を使って仮想フィールドを宣言し、クライアント設定で Kysely 式を使って実装します。選択、フィルタリング、並べ替えができ、ポリシーから参照することもできます。
model User {
id Int @id
postCount Int @computed
}
const db = new ZenStackClient(schema, {
dialect,
computedFields: {
User: {
postCount: (eb) =>
eb.selectFrom('Post')
.whereRef('Post.authorId', '=', 'id')
.select(({ fn }) => fn.countAll<number>().as('count')),
},
},
});
Prisma にはない ZModel の追加機能
generatorブロックはありません(zen generateとpluginを使用します)。type宣言とwithミックスイン、@@delegateによるポリモーフィズム、@jsonによる型付き JSON、@computedフィールドは、Prisma にはない ZenStack の追加機能です。- シングルクォートとダブルクォートのどちらも使用できます。
リファレンスドキュメント
このトピックに関する ZenStack のドキュメント全文は、references/ に同梱されています。
- modeling-overview.md — データモデリングの概念
- datasource.md —
datasourceブロック - model.md — モデル、フィールド、ID、デフォルト値
- attribute.md — フィールド属性とモデル属性
- relation.md — 1 対 1、1 対多、多対多、自己参照のリレーション
- enum.md — 列挙型
- custom-type.md —
type宣言 - mixin.md — 再利用可能なフィールドミックスイン(
with) - polymorphism.md —
@@delegateによるポリモーフィックモデル - typed-json.md — 強い型付けの JSON フィールド
- computed-fields.md —
@computedフィールド - zmodel-attribute-reference.md — 属性の完全なリファレンス
- zmodel-function-reference.md — 属性関数のリファレンス