- 后端
- 数据库
- GraphQL
【免费下载链接】prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]
导读
本文以 Prisma 服务端的数据建模为核心,讲解如何使用 GraphQL Schema Definition Language(SDL)编写datamodel.graphql数据模型,并通过prisma deploy将其转换为真实的数据库 Schema 与 Prisma GraphQL API。你将掌握对象类型、标量字段与类型修饰符、@unique/@default/@relation/@rename等指令的完整用法、系统字段与迁移技巧,以及关系(Relation)的删除行为(onDelete)建模,同时结合仓库源码理解数据模型在 Prisma CLI 内部是如何被解析与执行的。
一、Prisma 数据建模总览:用 SDL 描述你的数据模型
Prisma 使用 GraphQL 的 Schema Definition Language(SDL)进行数据建模。你的数据模型写在一个或多个.graphql文件中,它是 Prisma 在底层生成真实数据库 Schema的基础。如果只使用单个文件承载类型定义,这个文件通常被命名为datamodel.graphql。
包含数据模型的.graphql文件需要在prisma.yml中通过datamodel属性声明。例如:
datamodel: - types.graphql - enums.graphql如果只有一个文件定义数据模型,可以简写为:
datamodel: datamodel.graphql在仓库的 PrismaDefinition.ts 中,可以确认 CLI 对这一配置的解析逻辑:datamodel既支持字符串也支持字符串数组,CLI 会将其归一化为数组,然后逐个读取文件内容并拼接成一份完整的 SDL 文本(getTypesString)。如果某个路径指向的文件不存在,CLI 会直接抛出错误The types definition file "..." could not be found.。也就是说,datamodel属性指向的文件路径是相对于prisma.yml所在目录解析的(path.join(this.definitionDir, unresolvedTypesPath))。
数据模型是 Prisma 服务 GraphQL API 的基础:基于数据模型,Prisma 会生成一个强大的 GraphQL Schema(称为Prisma database schema),它为数据模型中的每个类型定义 CRUD 操作。
说明:GraphQL Schema 定义了一个 GraphQL API 的操作集合,本质上是用 SDL 编写的类型集合(SDL 还支持 interface、enum、union type 等更丰富的原语)。一个 GraphQL Schema 拥有三个特殊的根类型:
Query、Mutation和Subscription,它们定义了 API 的入口点与可接受的操作。
一个最小示例
一个简单的datamodel.graphql文件:
type Tweet { id: ID! @unique createdAt: DateTime! text: String! owner: User! location: Location! } type User { id: ID! @unique createdAt: DateTime! updatedAt: DateTime! handle: String! @unique name: String tweets: [Tweet!]! } type Location { latitude: Float! longitude: Float! }这个示例展示了数据建模中几个重要的核心概念:
- 三个类型
Tweet、User、Location会被映射为数据库中的表(table); User与Tweet之间存在一个双向关系;Tweet到Location存在一个单向关系;- 除了
User上的name字段,数据模型中所有字段都是必填的(类型后的!表示非空); id、createdAt、updatedAt字段由 Prisma 自动维护,并且在暴露出的 GraphQL API 中是只读的(无法通过 mutation 修改)。
创建和更新数据模型就像编写一个文本文件一样简单。当你对数据模型满意后,运行prisma deploy即可将变更应用到 Prisma 服务:
$ prisma deploy Changes: Tweet (Type) + Created type `Tweet` + Created field `id` of type `GraphQLID!` + Created field `createdAt` of type `DateTime!` + Created field `text` of type `String!` + Created field `owner` of type `Relation!` + Created field `location` of type `Relation!` + Created field `updatedAt` of type `DateTime!` User (Type) + Created type `User` + Created field `id` of type `GraphQLID!` + Created field `createdAt` of type `DateTime!` + Created field `updatedAt` of type `DateTime!` + Created field `handle` of type `String!` + Created field `name` of type `String` + Created field `tweets` of type `[Relation!]!` Location (Type) + Created type `Location` + Created field `latitude` of type `Float!` + Created field `longitude` of type `Float!` + Created field `id` of type `GraphQLID!` + Created field `updatedAt` of type `DateTime!` + Created field `createdAt` of type `DateTime!` TweetToUser (Relation) + Created relation between Tweet and User LocationToTweet (Relation) + Created relation between Location and Tweet Applying changes... (22/22) Applying changes... 0.4s注意prisma deploy输出的信息非常详尽:它逐个类型列出"新增的类型"和"新增的字段",并明确展示自动创建的关系(TweetToUser、LocationToTweet)。关系字段(owner、location、tweets)在输出中的类型被标记为Relation!或[Relation!]!,表明它们是关系字段而非标量字段。
数据模型的构建块
塑造数据模型有多种可用的构建块:
- Types(对象类型):由多个fields(字段)组成,用于对相似的实体进行分组。数据模型中的每个类型都会被映射到数据库,并在 GraphQL Schema 中生成对应的 CRUD 操作。
- Relations(关系):描述类型之间的关联语义。
- Interfaces(接口):抽象类型,包含一组字段,实现接口的类型必须包含这些字段。当前版本中接口还不能由用户自定义(相关能力处于待开发状态)。
- 特殊指令(directives):覆盖不同使用场景,例如类型约束或级联删除行为。
二、Prisma database schema 与 Data model 的区别
初学 GraphQL 与 Prisma 时,.graphql文件的数量容易让人困惑。理解每个文件的角色至关重要。
一般而言,一个.graphql文件可能包含以下两种内容之一:
- GraphQL 操作(即 query、mutation 或 subscription);
- 用 SDL 编写的 GraphQL 类型定义。
在区分 Prisma database schema 与 data model 的语境下,只有后者相关!
需要特别注意:并非所有属于后者的.graphql文件都是合法的 GraphQL Schema。如前所述,GraphQL Schema 的特征在于它除了 API 所需的其他类型外,还拥有三个根类型:Query、Mutation和Subscription。
按照这个定义,数据模型实际上并不是一个 GraphQL Schema——尽管它是以 SDL 编写的.graphql文件。它缺少根类型,因此并不真正定义 API 操作!Prisma 只是把数据模型当作一种方便的工具,让你能够表达数据模型长什么样。
随后,Prisma 会生成一个真正的 GraphQL Schema,其中包含Query、Mutation、Subscription根类型。这个 Schema 通常存储在项目内的prisma.graphql中,被称为Prisma database schema。注意:永远不要手动修改这个文件!
以如下极简数据模型为例:
datamodel.graphql
type User { id: ID! @uniue name: String! }将此数据模型部署到 Prisma 服务后,Prisma 会生成如下 Prisma database schema,它定义了服务的 GraphQL API:
prisma.graphql
type Query { users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]! user(where: UserWhereUniqueInput!): User } type Mutation { createUser(data: UserCreateInput!): User! updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User deleteUser(where: UserWhereUniqueInput!): User } type Subscription { user(where: UserSubscriptionWhereInput): UserSubscriptionPayload }注意:这是生成 Schema 的简化版本。可以看到,一个User类型直接派生出一整套 API:users/user查询、createUser/updateUser/deleteUser变更,以及user订阅。这就是"数据模型是 Prisma 服务 API 的基石"的直观体现。
补充:如果你已经研究过如何基于 Prisma 构建自己的 GraphQL 服务器,可能还会遇到另一个
.graphql文件,即你的application schema(应用 Schema)。它是另一个真正的 GraphQL Schema(同样包含Query、Mutation、Subscription根类型),定义了暴露给客户端应用的 API,并把底层的 Prisma GraphQL API 当作"查询引擎"来真正执行针对数据库的查询、变更与订阅。基于 Prisma 的 GraphQL 服务器通常有两个 GraphQL API,可以理解为服务的两个层级:
- 应用层(Application layer):由 application schema 定义(在这里实现业务逻辑、认证、与第三方服务集成等);
- 数据库层(Database layer):由 Prisma database service 定义。
三、对象类型(Object Types)
对象类型(或简称type)定义了数据模型中某一部分具体实体的结构,用来表示来自应用领域的实体。
如果你熟悉 SQL 数据库,可以把对象类型类比为关系数据库中**表(table)**的 Schema。一个类型拥有一个名称以及一个或多个字段(fields)。
一个类型的实例被称为一个node(节点)。这个术语指的是你**数据图(data graph)**中的一个节点。你在数据模型中定义的每个类型,都会作为对应的类型出现在生成的Prisma database schema中。
定义对象类型
在数据模型中使用关键字type定义对象类型:
type Article { id: ID! @unique text: String! isPublished: Boolean @default(value: "false") }上述类型具有以下属性:
- 名称:
Article - 字段:
id、text和isPublished(默认值为false)
从源码层面看,CLI 的数据模型解析器(见 parser.ts)只处理 SDL AST 中的ObjectTypeDefinition与EnumTypeDefinition两类定义;对于对象类型,它会逐一解析字段的name、type、isList(是否为 ListType)、isRequired(是否为 NonNullType)、defaultValue、relationName以及各类指令。这解释了为何数据模型中的类型名和字段名必须符合 SDL 语法,且非标量字段最终会通过resolveRelations(见 parser.ts)被解析为真实的对象类型引用。
类型生成的 API 操作
数据模型中的类型会影响 Prisma GraphQL API 中可用的操作。对每个类型而言:
- queries(查询):允许你获取该类型的一个或多个节点;
- mutations(变更):允许你创建、更新或删除该类型的节点;
- subscriptions(订阅):允许你在该类型节点发生变化时得到通知(例如新节点被创建,或已有节点被更新或删除)。
四、字段(Fields)
字段是类型的构建块,赋予节点形状。每个字段通过其名称被引用,并且要么是 标量 字段,要么是 关系 字段。
标量类型(Scalar Types)
仓库中的类型标识符表(见 scalar.ts)列出了 Prisma 数据模型支持的核心标量:String、Int、Float、Boolean、Long(内部使用)、DateTime、ID、UUID、Json。以下逐一说明这些标量的语义与使用规则。
String
String保存文本。它适用于用户名、博客文章内容等任何适合用文本表示的数据。
注意:在共享 demo cluster 上,String 值当前限制为 256KB。在其他集群上可以通过集群配置提高该限制。
在查询或变更中,String 字段必须使用双引号包裹:string: "some-string"。
Integer
Int是不能包含小数的数字。用于存储配料的重量、活动的最低年龄限制等值。
注意:Int的取值范围是 -2147483648 到 2147483647。
在查询或变更中,Int字段无需任何包裹字符:int: 42。
Float
Float是包含小数的数字。用于存储商品价格或复杂计算结果等值。
在查询或变更中,Float字段无需包裹字符且小数点可选:float: 42、float: 4.2。
Boolean
Boolean的值只能是true或false。适合跟踪设置项,例如用户是否希望收到邮件订阅、某道菜谱是否适合素食者。
在查询或变更中,Boolean字段无需包裹字符:boolean: true、boolean: false。
DateTime
DateTime类型用于存储日期或时间值,例如一个人的出生日期。
在查询或变更中,DateTime字段必须以 ISO 8601 格式并用双引号包裹:
datetime: "2015"datetime: "2015-11"datetime: "2015-11-22"datetime: "2015-11-22T13:57:31.123Z"
Enum
枚举在服务级别(service scope)上定义。
和 Boolean 类似,Enum 的值也只能是预定义集合中的一个。区别在于你可以自己定义可选值。例如,通过创建一个可能值为COMPACT、WIDE、COVER的枚举,可以规定文章应该如何排版。
注意:Enum 值最长不能超过 191 个字符。
在查询或变更中,Enum 字段无需包裹字符,且只能使用你为枚举定义的值:enum: COMPACT、enum: WIDE。
从解析器实现看,枚举类型同样会被解析成一种内部类型表示(isEnum: true),它的每个枚举值被当作一个字段处理(见 parser.ts)。
Json
有时你需要为松散结构的数据存储任意的 Json 值。Json类型会确保存储的内容确实是合法的 Json,并返回解析后的 Json 对象/数组(而非字符串)。
注意:在共享 demo cluster 上,Json 值当前限制为 256KB。其他集群可通过集群配置提高限制。
在查询或变更中,Json 字段必须用双引号包裹,特殊字符需要转义:json: "{\"int\": 1, \"string\": \"value\"}"。
ID
ID 值是基于 cuid 生成的 25 位唯一字符串。ID 字段是系统字段,仅用于内部使用,因此不能创建新的 ID 类型字段。
类型修饰符(Type Modifiers)
List(列表)
标量字段可以标记为列表字段类型。具有多对多(many)多重性的关系字段也会被标记为列表。
在查询或变更中,列表字段需要用方括号包裹,列表中的每个条目遵循上述相同的格式规则:listString: ["a string", "another string"]、listInt: [12, 24]。
Required(必填)
字段可以标记为必填(有时也称为"非空")。在创建新节点时,对于必填且没有默认值的字段,你必须提供值。
必填字段使用字段类型后的!标记:name: String!。
字段约束(Field Constraints)
字段可以配置特定的约束条件,为数据模型增加更多语义。
Unique(唯一)
设置unique约束可确保同一类型的两个节点不能在该字段上拥有相同的值。唯一的例外是null值,即多个节点可以都为null而不会违反约束。
典型例子是
User类型上的User都应拥有全局唯一的邮箱地址。
请注意:String 字段只有前 191 个字符参与唯一性检查,且唯一性检查不区分大小写。如果两个字符串的前 191 个字符相同,或者仅大小写不同,则无法同时存储。
标记字段唯一只需在其后追加@unique指令:
type User { email: String! @unique age: Int! }对于每个标注了@unique的字段,你都可以通过为该字段提供一个值来查询对应节点。
例如,对于上述数据模型,你现在可以通过email地址获取特定的User节点:
query { user(where: { email: "alice@graph.cool" }) { age } }从源码看,@unique指令由解析器中的isUniqe方法识别(见 parser.ts),并且id字段会被自动视为唯一(const isUnique = isId || this.isUniqe(field),见 parser.ts)。同时,指令名常量unique、default、relation等统一定义在 directives.ts 中。
更多约束
更多数据库约束将根据功能需求在未来逐步加入。
默认值(Default Value)
你可以为非列表标量字段设置默认值。当创建新节点时未提供值,将使用该默认值。
为字段指定默认值使用@default指令:
type Story { isPublished: Boolean @default(value: "false") someNumber: Int! @default(value: "42") title: String! @default(value: "My New Post") publishDate: DateTime! @default(value: "2018-01-26") }注意:即使是非字符串类型(如Boolean或Int),始终需要将值放在双引号中。这一点与解析器实现一致:@default的value参数统一按字符串字面量读取(见 parser.ts 中getDefaultValue对参数值的提取方式)。
系统字段(System Fields)
id、createdAt、updatedAt这三个字段具有特殊含义。它们在数据模型中是可选的,但会始终在底层数据库中被维护。因此你可以在之后随时把字段添加到数据模型中,已有节点的数据仍然可用。
目前这些字段的值在 GraphQL API 中是只读的(导入数据时除外),未来将允许配置。
重要警告:你不能拥有名为id、createdAt、updatedAt的自定义字段,因为这些名称被系统字段保留。以下是这三个字段唯一支持的声明形式:
id: ID! @uniquecreatedAt: DateTime!updatedAt: DateTime!
在解析器层面,这三个保留字段名定义于 legacyFields.ts,关系型模型解析器 relationalParser.ts 通过字段名或指令识别它们,并据此将字段标记为只读(isReservedReadOnlyField,见 parser.ts)。
系统字段:id
节点创建时会自动获得一个全局唯一标识符,存储于id字段。
每当你将id字段添加到类型定义中以便在 GraphQL API 中暴露它时,必须用@unique指令标注。
id具有以下属性:
- 由 25 个字母数字字符组成(字母始终为小写);
- 始终以(小写)字母
c开头; - 遵循 cuid(collision resistant unique identifiers,防碰撞唯一标识符)方案。
注意:你的所有对象类型在数据库 Schema 中都会实现Node接口。Node接口如下:
interface Node { id: ID! @unique }系统字段:createdAt与updatedAt
数据模型还提供两个特殊字段,你可以添加到类型中:
createdAt: DateTime!:记录该对象类型的节点被创建的确切日期和时间;updatedAt: DateTime!:记录该对象类型的节点最后被更新的确切日期和时间。
如果你希望类型暴露这些字段,只需将它们添加到类型定义中,例如:
type User { id: ID! @unique createdAt: DateTime! updatedAt: DateTime! }字段生成的 API 操作
数据模型中的字段会影响 Prisma GraphQL API 中可用的查询参数(query arguments)。
迁移标量字段的值
你可以使用updateManyXsmutation 为所有节点、或仅某一特定子集的节点迁移标量字段的值:
mutation { # 将所有没有邮箱地址的用户的 email 更新为空字符串 updateManyUsers( where: { email: null } data: { email: "" } ) }向数据模型添加必填字段
当向已包含节点的模型中添加必填字段时,你会收到如下错误信息:
You are creating a required field but there are already nodes present that would violate that constraint.
这是因为所有节点的该字段都将为null。添加必填字段需要以下步骤:
- 先以可选方式添加该字段;
- 使用
updateManyXs将所有节点的该字段从null迁移为非空值; - 现在将该字段标记为必填并正常部署。
五、关系(Relations)
关系定义了 类型 之间连接(connection)的语义。两个类型通过一个 关系字段 相连。当关系可能存在歧义时,需要为该关系字段标注@relation指令来消除歧义。
关系也可以连接类型与其自身,此时被称为self-relation(自关系)。
必填关系(Required Relations)
对于to-one关系字段,你可以配置它是必填还是可选。必填标记在 GraphQL 中充当一种契约,表明该字段永远不会是null。因此,用户地址字段的类型应为Address或Address!。
包含必填to-one关系字段的类型的节点,只能通过 嵌套 mutation 创建,以确保对应字段不会为null。
注意:to-many关系字段始终是必填的。例如,一个包含多个用户地址的字段总是使用类型
[Address!]!,永远不能是[Address!]。原因在于:当字段不包含任何节点时,会返回[](空数组),而[]并不是null。
@relation指令
在类型之间定义关系时,可以使用@relation指令为关系提供元信息。它接受两个参数:
name:该关系的标识符(以字符串形式提供)。仅当关系存在歧义时才需要此参数。注意:每次使用@relation指令时都必须提供name参数。onDelete:指定删除行为并启用级联删除。当一个带有相关节点的节点被删除时,删除行为决定相关节点的命运。该参数的值定义为一个枚举,可能的取值如下:SET_NULL(默认):将相关节点设为null;CASCADE:删除相关节点。注意:不能将双向关系的两端都设置为CASCADE。
以下是使用@relation指令的数据模型示例:
type User { id: ID! @unique stories: [Story!]! @relation(name: "StoriesByUser" onDelete: CASCADE) } type Story { id: ID! @unique text: String! author: User @relation(name: "StoriesByUser") }该示例中的删除行为如下:
- 当一个
User节点被删除时,其所有相关的Story节点也会被删除; - 当一个
Story节点被删除时,它只会从相关User节点的stories列表中被移除。
从源码层面看,@relation指令的name参数通过getRelationName提取(见 parser.ts),并存储在字段的relationName属性中。在resolveRelations阶段,解析器会优先通过relationName配对关系字段(若名称相同则视为同一关系),再对没有指令标注、但结构上唯一可配对的关系字段进行推断连接(见 parser.ts)。这也印证了文档中"歧义关系必须命名"的规则:当同一类型对之间存在多个关系时,若不做命名,解析器无法确定字段如何配对。
省略@relation指令
在最简单的情况下——两个类型之间的关系没有歧义,且使用默认删除行为(SET_NULL)——相应的关系字段不必标注@relation指令。
下面在User与Story之间定义了一个双向one-to-many关系。由于未提供onDelete,使用默认删除行为SET_NULL:
type User { id: ID! @unique stories: [Story!]! } type Story { id: ID! @unique text: String! author: User }该示例中的删除行为如下:
- 当一个
User节点被删除时,其所有相关Story节点的author字段会被设为null。注意:如果author字段被标记为必填,该操作将导致错误; - 当一个
Story节点被删除时,它只会从相关User节点的stories列表中被移除。
使用@relation指令的name参数
在某些情况下,数据模型可能包含歧义关系。例如,你不仅想用关系表达User与Story之间的"作者关系",还想表达哪些Story节点被User点赞。
这时User与Story之间就存在两个不同的关系!为了消除歧义,你需要给关系命名:
type User { id: ID! @unique writtenStories: [Story!]! @relation(name: "WrittenStories") likedStories: [Story!]! @relation(name: "LikedStories") } type Story { id: ID! @unique text: String! author: User! @relation(name: "WrittenStories") likedBy: [User!]! @relation(name: "LikedStories") }如果在此例中未提供name,将无法判断writtenStories应该关联author字段还是likedBy字段。
使用@relation指令的onDelete参数
如前所述,你可以为相关节点指定专门的删除行为,这正是@relation指令onDelete参数的用途。
考虑以下示例:
type User { id: ID! @unique comments: [Comment!]! @relation(name: "CommentAuthor", onDelete: CASCADE) blog: Blog @relation(name: "BlogOwner", onDelete: CASCADE) } type Blog { id: ID! @unique comments: [Comment!]! @relation(name: "Comments", onDelete: CASCADE) owner: User! @relation(name: "BlogOwner", onDelete: SET_NULL) } type Comment { id: ID! @unique blog: Blog! @relation(name: "Comments", onDelete: SET_NULL) author: User @relation(name: "CommentAuthor", onDelete: SET_NULL) }让我们逐一分析三个类型的删除行为:
- 当一个
User节点被删除时:- 所有相关的
Comment节点将被删除; - 相关的
Blog节点将被删除。
- 所有相关的
- 当一个
Blog节点被删除时:- 所有相关的
Comment节点将被删除; - 相关
User节点的blog字段将被设为null。
- 所有相关的
- 当一个
Comment节点被删除时:- 相关
Blog节点继续存在,被删除的Comment节点从它的comments列表中移除; - 相关
User节点继续存在,被删除的Comment节点从它的comments列表中移除。
- 相关
注意示例中User.blog与Blog.owner构成的双向关系两端分别设置了CASCADE与SET_NULL——这正是文档强调的"双向关系两端不能同时为CASCADE"的典型应用。
关系生成的 API 操作
数据模型中的关系会影响 GraphQL API 中可用的操作。对每个关系而言:
- relation queries(关系查询):允许你跨类型查询数据,或针对关系进行聚合查询(也可以使用 Relay 的 connection model);
- nested mutations(嵌套变更):允许你跨类型创建(create)、连接(connect)、更新(update)、upsert 和删除(delete)节点;
- relation subscriptions(关系订阅):允许你在关系发生变化时得到通知。
六、GraphQL 指令(Directives)
指令用于为数据模型提供额外信息。它们的写法是@name(argument: "value"),当没有参数时则简写为@name。
数据模型指令(Data Model Directives)
数据模型指令描述 GraphQL Schema 中类型或字段的附加信息。
唯一标量字段(Unique scalar fields)
@unique指令将标量字段标记为唯一。唯一字段将在底层数据库中应用唯一索引。
# `User` 类型拥有唯一的 `email` 字段 type User { email: String @unique }关系字段(Relation fields)
@relation(name: String, onDelete: ON_DELETE! = CASCADE)指令可以附加到关系字段上。
(详见上文 @relation 指令 一节。)
标量字段默认值(Default value for scalar fields)
@default(value: String!)指令为标量字段设置默认值。注意:对所有标量字段而言,value参数的类型都是 String(即使字段本身不是字符串):
# `title`、`published` 和 `someNumber` 字段分别具有默认值 `New Post`、`false` 和 `42` type Post { title: String! @default(value: "New Post") published: Boolean! @default(value: "false") someNumber: Int! @default(value: "42") }临时指令(Temporary Directives)
临时指令用于执行一次性迁移操作。在部署了包含临时指令的服务之后,需要手动将其从类型定义文件中移除。
重命名类型或字段(Renaming a type or field)
临时指令@rename(oldName: String!)用于重命名类型或字段。
# 将 `Post` 类型重命名为 `Story`,并将其 `text` 字段重命名为 `content` type Story @rename(oldName: "Post") { content: String @rename(oldName: "text") }警告:如果不使用 rename 指令,Prisma 会先移除旧类型和字段,再创建新的类型和字段,从而导致数据丢失!
七、命名约定(Naming Conventions)
在 Prisma 服务中,你会遇到不同类型的对象(如类型或关系),它们遵循各自的命名约定,以帮助你区分。
类型(Types)
类型名称决定了派生的查询与变更名称,以及嵌套变更的参数名称。类型名称只能包含字母数字字符,并且需要以大写字母开头。它们最多可包含64 个字符。
建议使用单数形式的类型名称。
类型名称在服务级别上是唯一的。
示例:Post、PostCategory
标量字段与关系字段(Scalar and relation fields)
标量字段的名称用于查询以及变更的查询参数中。字段名称只能包含字母数字字符,并且需要以小写字母开头。它们最多可包含64 个字符。
关系字段的名称遵循相同的约定,并决定关系变更(relation mutations)的参数名称。
建议只为列表字段选择复数名称。
字段名称在类型级别上是唯一的。
示例:name、email、categoryTags
关系(Relations)
关系名称只能包含字母数字字符,并且需要以大写字母开头。它们最多可包含64 个字符。
关系名称在服务级别上是唯一的。
示例:
UserOnPost、UserPosts或PostAuthor,字段名为user和posts;Appointments、EmployeeOnAppointment或AppointmentEmployee,字段名为employee和appointments。
枚举(Enums)
枚举值只能包含字母数字字符和下划线,并且需要以大写字母开头。枚举值的名称可用于查询过滤器和变更中。它们最多可包含191 个字符。
枚举名称在服务级别上是唯一的。
枚举值名称在枚举级别上是唯一的。
示例:A、ROLE_TAG、RoleTag
八、更多 SDL 特性
本节介绍 Prisma 数据建模尚未支持的更多 SDL 特性。
接口(Interfaces)
"与许多类型系统一样,GraphQL 支持接口。接口是一种抽象类型,包含一组字段,实现该接口的类型必须包含这些字段。"——引自官方 GraphQL 文档。
说明:Prisma 对接口的支持处于待开发状态,暂不能由用户自定义接口类型。
联合类型(Union Types)
"联合类型与接口非常相似,但它们不能在类型之间指定任何公共字段。"——引自官方 GraphQL 文档。
说明:联合类型同样处于待开发状态,暂不支持用于 Prisma 数据建模。
九、快速上手路线:从数据模型到可用 API
综合以上内容,一个完整的 Prisma 数据建模工作流可以概括为:
- 编写数据模型:在
datamodel.graphql(或按prisma.yml中datamodel属性声明的多个.graphql文件)中用 SDL 定义类型、字段、约束与关系; - 声明数据模型:在
prisma.yml中通过datamodel属性指向上述文件(CLI 解析逻辑见 PrismaDefinition.ts); - 部署:运行
prisma deploy,Prisma 会输出变更计划并生成数据库 Schema 与prisma.graphql(Prisma database schema),为每个类型自动派生查询、变更与订阅操作; - 消费 API:在应用层使用生成的 GraphQL API(或在此基础上构建你自己的 application schema)读写数据。
在整个过程中,请牢记几个关键约束:id/createdAt/updatedAt是保留的系统字段且只读;@relation的name参数在歧义关系中必不可少;双向关系的两端不能同时设置onDelete: CASCADE;@rename用于避免重命名导致的数据丢失;所有非字符串标量的默认值也需用双引号包裹。
十、延伸阅读
- 数据模型相关指令的源码定义:directives.ts
- 数据模型解析器实现(指令、默认值、关系解析):parser.ts
- 关系型数据库模型解析器(保留字段识别):relationalParser.ts
- 文档型数据库模型解析器(内嵌类型识别):documentParser.ts
- 标量类型标识符表:scalar.ts
prisma.yml中datamodel属性的解析与校验:PrismaDefinition.ts- Prisma GraphQL API 的查询、变更与订阅参考:03-Prisma-API
版本说明:本文内容基于当前仓库中
docs/1.14版本文档,其中的版本特性(如 String/Json 256KB 限制、@rename临时指令、updateManyXs迁移 mutation 等)以该版本为准。
- 后端
- 数据库
- GraphQL
【免费下载链接】prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]
相关推荐
Prisma 数据建模(SDL)完全指南:用 GraphQL SDL 设计数据模型、字段约束与关系
Prisma 数据建模(SDL)完全指南:用 GraphQL SDL 设计数据模型、字段约束与关系 导读 本文是 Prisma 1.x 数据建模(Data Mo
后端数据库GraphQLPrisma 数据建模完全指南:基于 GraphQL SDL 设计数据模型(Data Modelling)
Prisma 数据建模完全指南:基于 GraphQL SDL 设计数据模型(Data Modelling) 导读 本文以 Prisma 1.x 官方参考文档《D
后端数据库GraphQLPrisma 数据建模(SDL)完全指南:datamodel.graphql 类型、字段、关系与指令详解
Prisma 数据建模(SDL)完全指南:datamodel.graphql 类型、字段、关系与指令详解 导读 本篇指南以 docs/1.4/04 Refere
后端数据库GraphQL
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考