Prisma 数据建模完全指南:用 GraphQL SDL 编写 Data Model 并生成数据库 Schema
后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载导读Prisma 使用 GraphQL 的 Schema Definition LanguageSDL作为数据建模语言你只需在一个或多个.graphql文件中用type关键字声明数据模型prisma deploy便会据此在底层数据库中创建真实的表结构并为每个类型自动生成包含 CRUD 查询、变更与订阅的 GraphQL API即 Prisma database schema。本文以 docs/1.2/04-Reference/02-Service-Configuration/03-Data-Modelling-(SDL).md.md) 为主体结合仓库内prisma-datamodel的源码实现系统讲解数据模型的全部构建块——类型、字段、标量类型、指令、关系与命名规范读完你即可独立编写、部署并维护一个完整的 Prisma 服务数据模型。Overview什么是 Prisma 的数据模型Prisma 采用 GraphQL SDL 进行数据建模。你的数据模型data model书写在一个或多个.graphql文件中它是 Prisma 在底层生成真实数据库 Schema 的唯一依据数据模型中的每个类型都会映射为数据库中的一张表并成为最终 GraphQL API 中 CRUD 操作的基础。如果只使用一个文件定义类型该文件通常被命名为datamodel.graphql。在 prisma.yml 中声明数据模型包含数据模型的文件必须在prisma.yml的datamodel属性中登记。支持多文件形式datamodel: - types.graphql - enums.graphql也支持单文件简写datamodel: datamodel.graphql从源码实现看datamodel属性是 deploy 流程的必填项。deploy 命令实现中明确检查如果prisma.yml缺少该属性会直接报错The property datamodel is missing in your prisma.yml。也就是说没有数据模型声明Prisma 服务将无法部署。prisma.yml的完整字段说明可参考仓库内的 Service Configuration 配置文档。数据模型与 GraphQL API 的关系数据模型是 Prisma 服务 GraphQL API 的基石。基于数据模型Prisma 会生成一个功能强大的 GraphQL Schema即Prisma database schema为数据模型中的每个类型定义对应的 CRUD 操作。什么是 GraphQL SchemaGraphQL Schema 定义了一个 GraphQL API 的操作集合本质上是使用 SDL 书写的类型集合SDL 还支持接口、枚举、联合类型等原语。一个完整的 GraphQL Schema 拥有三个特殊的根类型Query、Mutation和Subscription它们定义了 API 的入口点决定了 API 接受哪些操作。完整示例以下是一个简单的datamodel.graphqltype 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三个类型会被映射为数据库中的表User与Tweet之间存在双向关系User.tweets↔Tweet.ownerTweet到Location之间存在单向关系除User.name外所有字段都是必填的由类型后的!标识id、createdAt、updatedAt字段由 Prisma 托管在暴露的 GraphQL API 中是只读的无法通过 mutation 修改。应用数据模型prisma deploy创建和更新数据模型就像写一个文本文件一样简单。对数据模型满意后运行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注意 deploy 输出中关系会被命名Prisma 自动为Tweet ↔ User生成关系TweetToUser为Tweet ↔ Location生成关系LocationToTweet。这也印证了仓库中 datamodel 模型定义 里IGQLType.isRelationTable、IGQLField.relatedField用于标记双向关系等内部属性的作用——Prisma 在解析数据模型时会将字段划分为GQLScalarField、GQLOneRelationField和GQLMultiRelationField三种内部类型分别对应标量字段、to-one 关系字段和 to-many 关系字段后者自动设置isList true。数据模型的构建块数据模型由以下构建块组成类型Types由多个字段组成用于将相似的实体归组。数据模型中的每个类型都会映射到数据库并在 GraphQL Schema 中加入对应的 CRUD 操作关系Relations描述类型之间的关联关系接口Interfaces抽象类型包含实现该接口的类型必须包含的一组字段。目前接口尚不能由用户自定义属于未支持的特性指令Directives覆盖类型约束、级联删除等不同用例的特殊指令。下文将逐一详解这些构建块。Prisma database schema 与 Data model 的区别刚接触 GraphQL 和 Prisma 时容易混淆手头多个.graphql文件的角色。理解每个文件的职责至关重要。一般来说一个.graphql文件可能包含两类内容GraphQL 操作即 query、mutation 或 subscription使用 SDL 书写的 GraphQL 类型定义。在区分 Prisma database schema 与数据模型时只有后者相关。需要注意的是并非所有 SDL 类型定义文件都是合法的 GraphQL Schema。如前述 InfoBox 所述一个 GraphQL Schema 的特征是拥有Query、Mutation、Subscription三个根类型以及 API 所需的其它类型。按这个定义数据模型实际上并不是一个 GraphQL Schema——尽管它是 SDL 书写的.graphql文件但它缺少根类型因此不定义任何 API 操作。Prisma 只是把数据模型当作一个便捷工具让你以声明式方式表达数据的结构。随后 Prisma 会生成一个真正的 GraphQL Schema其中包含Query、Mutation、Subscription根类型。这个 Schema 通常以prisma.graphql的形式存放在项目内被称为Prisma database schema。注意永远不要手动修改这个文件。例如考虑如下极其简单的数据模型datamodel.graphqltype User { id: ID! uniue name: String! }注原文示例中uniue为笔误应为unique否则id字段无法通过校验。将该数据模型部署到 Prisma 服务后Prisma 会生成如下定义该服务 GraphQL API 的 Prisma database schemaprisma.graphqltype 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 的简化版本实际生成的 Schema 还包含UserWhereInput、UserCreateInput、UserUpdateInput、UserWhereUniqueInput等大量配套输入类型。可见数据模型中的一个User类型直接衍生出users/user两个查询、createUser/updateUser/deleteUser三个变更以及一个user订阅。这正是 Prisma GraphQL API 的基础。应用层与数据库层两种 GraphQL API如果你已经尝试过基于 Prisma 构建自己的 GraphQL 服务器可能还会遇到另一个.graphql文件——application schema。它同样是一个完整的 GraphQL Schema包含三个根类型定义了对客户端应用暴露的 API并把底层的 Prisma GraphQL API 当作查询引擎来真正执行针对数据库的查询、变更和订阅。 一个基于 Prisma 的 GraphQL 服务器通常拥有两个 GraphQL API可以把它们理解为服务的两层应用层由 application schema 定义在这里实现业务逻辑、鉴权、与第三方服务集成等数据库层由 Prisma database service 定义。Object types对象类型对象类型简称类型定义了数据模型中某一块具体内容的结构用于表示来自应用领域的实体。如果你熟悉 SQL 数据库可以把对象类型理解为关系型数据库中一张表的 Schema。一个类型拥有名称和一个或多个字段。类型的实例被称为node节点指数据图data graph中的一个节点。你在数据模型中定义的每个类型都会在生成的 Prisma database schema 中以对等类型出现。定义对象类型在数据模型中使用type关键字定义对象类型type Article { id: ID! unique text: String! isPublished: Boolean default(value: false) }上述类型具有以下属性名称Article字段id、text和isPublished默认值为false类型对应的 API 操作数据模型中的类型会影响 Prisma GraphQL API 中可用的操作。对每个类型queries允许获取该类型的一个或多个节点mutations允许创建、更新或删除该类型的节点subscriptions允许订阅该类型节点的变更通知即节点被创建、更新或删除。从内部实现看Prisma 的数据模型解析器会将每个类型解析为 IGQLType记录其名称、字段列表、是否是枚举、是否是关系表isRelationTable、是否内嵌isEmbedded等元信息供后续生成数据库 Schema 和 GraphQL API 使用。Fields字段字段是类型的构建块赋予节点形状。每个字段通过名称引用要么是标量字段要么是关系字段。标量类型Scalar types仓库中 scalar.ts 的TypeIdentifier定义了 Prisma 数据模型支持的标量类型集合String、Int、Float、Boolean、Long、DateTime、ID、UUID、Json。以下是文档对各类型的具体说明。StringString用于存放文本适合用户名、博客文章内容等以文本表示的数据。注意在共享 demo cluster 上String 值当前被限制为最大 256KB该限制可通过集群配置在其它 cluster 上提高。在查询或变更中String 字段必须使用双引号包裹string: some-string。IntegerInt是不能包含小数的数字适合存储配方的配料重量、活动的最低年龄等。注意Int的取值范围是 -2147483648 到 2147483647。在查询或变更中Int字段无需任何包裹字符int: 42。FloatFloat是包含小数的数字适合存储商品价格或复杂计算结果。在查询或变更中Float字段无需包裹字符且小数点可选float: 42、float: 4.2。BooleanBoolean的值为true或false适合记录是否订阅邮件、是否适合素食者等设置。在查询或变更中Boolean字段无需包裹字符boolean: true、boolean: false。DateTimeDateTime用于存储日期或时间值例如一个人的出生日期。在查询或变更中DateTime字段必须以 ISO 8601 格式并用双引号包裹datetime: 2015datetime: 2015-11datetime: 2015-11-22datetime: 2015-11-22T13:57:31.123ZEnum枚举Enum在服务作用域上定义。与 Boolean 类似Enum 只能取预定义集合中的一个值区别在于你可以自己定义可能的取值。例如可以创建一个取值仅为COMPACT、WIDE、COVER的 Enum 来指定文章的排版格式。注意Enum 值最长不能超过 191 个字符。在查询或变更中Enum 字段无需包裹字符且只能使用你为该枚举定义的值enum: COMPACT、enum: WIDE。JSON有时需要为松散结构的数据存储任意 JSON 值。JSON类型会确保存入的是合法 JSON并返回解析后的 Json 对象/数组而不是字符串。注意Json 值在共享 demo cluster 上同样被限制为最大 256KB。在查询或变更中Json 字段需要用双引号包裹特殊字符需要转义json: {\int\: 1, \string\: \value\}。IDID 值是基于 [cuid] 生成的 25 位唯一字符串。ID 字段属于系统字段仅内部使用因此无法创建类型为 ID 的自定义字段。仓库中的 relationalParser.ts 也印证了这一点id、createdAt、updatedAt这三个保留字段名会被解析器专门识别isIdField、isCreatedAtField、isUpdatedAtField。类型修饰符Type modifiersList列表标量字段可以标记为列表类型。多对多关系中的字段也会被标记为列表。在查询或变更中列表字段需要用方括号包裹列表内的各项遵循与前述一致的格式规则listString: [a string, another string]、listInt: [12, 24]。Required必填字段可以被标记为必填有时也称 non-null。创建新节点时对于必填且没有默认值的字段必须提供值。必填字段通过在字段类型后添加!标记name: String!。字段约束Field constraintsUnique唯一设置unique约束可确保同一类型的两个节点在某个字段上不能有相同的值。唯一的例外是null多个节点可以同时持有null而不违反约束。典型例子是User类型上的email字段——假设每个User的邮箱地址全局唯一。注意String 字段只有前 191 个字符参与唯一性判断且唯一性检查不区分大小写。如果两个字符串的前 191 个字符相同或仅大小写不同则无法同时存储。标记字段唯一只需在其后追加unique指令type User { email: String! unique age: Int! }对于每个标注了unique的字段你都可以通过为该字段提供值来查询对应节点。例如针对上面的数据模型可以按email获取特定的User节点query { user(where: { email: alicegraph.cool }) { age } }这正是生成 Schema 中user(where: UserWhereUniqueInput!): User查询的来源——unique字段会进入UserWhereUniqueInput成为按唯一字段定位节点的入口。更多约束更多数据库约束将根据功能需求后续添加。默认值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) }注意即使字段本身不是 String 类型如Boolean或Int默认值也必须用双引号包裹。从源码看默认值会被解析进字段模型 IGQLField.defaultValue由 Prisma 在创建节点时自动填充。默认值的存在也放宽了 Required 约束必填但带默认值的字段在创建节点时可以不显式提供值。系统字段System fieldsid、createdAt、updatedAt三个字段具有特殊含义。它们在数据模型中是可选的但始终会在底层数据库中维护。因此你可以随时在数据模型中加入这些字段已有节点对应的数据会立即可用。目前这些字段的值在 GraphQL API 中只读数据导入场景除外未来可能变得可配置。⚠️警告你不能创建名为id、createdAt、updatedAt的自定义字段这些名称被系统字段保留。以下是这三个字段唯一支持的声明形式id: ID! uniquecreatedAt: DateTime!updatedAt: DateTime!系统字段id节点创建时会自动分配一个全局唯一标识符存储于id字段。每当你在类型定义中添加id字段以在 GraphQL API 中暴露它时必须用unique指令标注。id具有以下属性由 25 个字母数字字符组成字母总是小写总是以小写字母c开头遵循 cuidcollision resistant unique identifiers抗碰撞唯一标识符方案。注意你的所有对象类型在 database schema 中都会实现Node接口。Node接口如下interface Node { id: ID! unique }系统字段createdAt和updatedAt数据模型还提供两个可添加到类型上的特殊字段createdAt: DateTime!存储该对象类型节点被创建的确切日期和时间updatedAt: DateTime!存储该对象类型节点被最后更新的确切日期和时间。如果希望类型暴露这些字段只需将它们加入类型定义例如type User { id: ID! unique createdAt: DateTime! updatedAt: DateTime! }在内部DirectiveKeys 中定义了id、createdAt、updatedAt等保留指令键解析器会据此识别这些系统字段并在生成数据库 Schema 时为其创建对应的列与索引。字段对应的 API 操作数据模型中的字段会影响可用的查询参数query arguments——每个标量字段都会进入WhereInput、OrderByInput等过滤、排序参数体系中。Relations关系关系定义了两种类型之间连接connection的语义。两个类型通过关系字段相连。当关系存在歧义时关系字段需要用relation指令来消除歧义。关系也可以连接类型与自身此时被称为自关系self-relation。必填关系Required relations对于to-one关系字段可以配置它是必填还是可选。必填标记是 GraphQL 层面的契约保证该字段永远不会为null。例如用户地址字段的类型为Address可选或Address!必填。包含必填 to-one 关系字段的类型的节点只能通过嵌套变更创建以确保对应字段不会为null。注意to-many关系字段总是必填的。例如包含多个用户地址的字段始终使用[Address!]!类型绝不可能是[Address!]。原因在于当字段不包含任何节点时返回[]而[]不是null。relation指令定义类型间关系时可使用relation指令提供关于关系的元信息。它接受两个参数name该关系的标识符以字符串提供。仅在关系存在歧义时才必需。注意只要使用relation指令name参数就是必需的onDelete指定删除行为支持级联删除。当一个带有相关节点的节点被删除时删除行为决定相关节点的命运。该参数的输入值由一个枚举定义可选值如下SET_NULL默认将相关节点设置为nullCASCADE删除相关节点。注意双向关系的两端不能同时设为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指令在关系无歧义、且应使用默认删除行为SET_NULL的最简单场景下对应关系字段不必标注relation指令。这里定义User与Story之间的双向一对多关系。由于未提供onDelete使用默认删除行为SET_NULLtype 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字段。从源码实现看关系名会被记录在字段模型的 IGQLField.relationName 属性中——该属性仅在通过指令指定了关系名时才会被设置而双向关系的另一端则通过relatedField指针关联。使用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列表中移除。注意同一关系BlogOwner的两端行为不同User.blog端是CASCADE删 User 连带删 Blog而Blog.owner端是SET_NULL删 Blog 只清空 User.blog 引用——这也体现了双向关系两端不能同时为 CASCADE的约束。关系对应的 API 操作数据模型中的关系会影响 Prisma GraphQL API 中可用的操作。对每个关系关系查询relation queries允许跨类型查询数据或针对关系做聚合查询也可使用 Relay 的连接模型嵌套变更nested mutations允许跨类型创建create、连接connect、更新update、upsert 和删除delete节点关系订阅relation subscriptions允许订阅关系变更的通知。GraphQL directives指令指令用于在数据模型中提供附加信息。它们的形式为name(argument: value)无参数时简写为name。仓库中的 directives.ts 汇总了数据模型解析器识别的指令键unique、default、relation、db、index、indexes、sequence、relationTable、scalarList以及系统字段相关的id、createdAt、updatedAt、embedded。数据模型指令Data model directives数据模型指令用于描述 GraphQL Schema 中类型或字段的附加信息。唯一标量字段unique指令将标量字段标记为唯一。唯一字段会在底层数据库中应用唯一索引。# User 类型有一个唯一的 email 字段 type User { email: String unique }更多关于unique指令的信息见上文唯一约束一节。关系字段指令relation(name: String, onDelete: ON_DELETE! NO_ACTION)可以附加到关系字段上。详见上文relation指令一节。标量字段默认值指令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临时指令用于执行一次性迁移操作。部署包含临时指令的服务后必须手动将其从类型定义文件中移除。重命名类型或字段临时指令rename(oldName: String!)用于重命名类型或字段。# 将 Post 类型重命名为 Story并将其 text 字段重命名为 content type Story rename(oldName: Post) { content: String rename(oldName: text) }⚠️警告如果不使用重命名指令Prisma 会先删除旧类型和旧字段再创建新类型和新字段导致数据丢失迁移标量字段的值临时指令migrationValue(value: String!)用于迁移标量字段的值。当把可选字段改为必填字段时必须同时使用该指令。命名规范Naming conventionsPrisma 服务中遇到的不同对象如类型、关系遵循各自的命名规范便于区分。类型Types类型名决定了派生的查询和变更名称以及嵌套变更的参数名。类型名只能包含字母数字字符且必须以大写字母开头长度最多 64 个字符。建议选择单数形式的类型名。类型名在服务级别唯一。示例PostPostCategory标量与关系字段Scalar and relation fields标量字段名用于查询和变更的查询参数中。字段名只能包含字母数字字符且必须以小写字母开头长度最多 64 个字符。关系字段名遵循相同规范并决定关系变更的参数名。建议只为列表字段选择复数名称。字段名在类型级别唯一。示例nameemailcategoryTags关系Relations关系名只能包含字母数字字符且必须以大写字母开头长度最多 64 个字符。关系名在服务级别唯一。示例UserOnPost、UserPosts或PostAuthor配以字段名user和postsAppointments、EmployeeOnAppointment或AppointmentEmployee配以字段名employee和appointments枚举Enums枚举值只能包含字母数字字符和下划线且必须以大写字母开头。枚举值名可用于查询过滤器和变更中长度最多 191 个字符。枚举名在服务级别唯一。枚举值名在枚举级别唯一。示例AROLE_TAGRoleTag更多 SDL 特性尚未支持本节描述 Prisma 数据建模尚未支持的 SDL 特性。接口Interfaces与许多类型系统一样GraphQL 支持接口。接口是一种抽象类型包含实现该接口的类型必须包含的一组字段。引自官方 GraphQL 文档注意要了解接口何时以及如何引入 Prisma可关注相关的功能请求feature request。联合类型Union types联合类型与接口非常相似但联合类型不能为各类型指定公共字段。引自官方 GraphQL 文档注意要了解联合类型何时以及如何引入 Prisma可关注相关的功能请求。小结在 Prisma 中数据建模完全发生在文本世界用 SDL 编写datamodel.graphql在prisma.yml中登记路径运行prisma deploy即可获得底层数据库表和功能完整的 GraphQL API。本文覆盖了数据模型的全部构建块——从标量类型、类型修饰符、字段约束、系统字段到关系的方向性、relation指令的name与onDelete语义再到unique/default/rename/migrationValue等指令及命名规范并辅以prisma-datamodel源码model.ts、scalar.ts、directives.ts佐证其内部解析逻辑。掌握这些规则后你可以继续深入阅读仓库中的 Prisma GraphQL API 参考理解数据模型生成的查询、变更与订阅如何在实际应用中被调用从而完成从建模到查询的完整闭环。赞分享后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载相关推荐Prisma 数据建模指南用 GraphQL SDL 设计数据模型Data ModellingPrisma 数据建模指南用 GraphQL SDL 设计数据模型Data Modelling 导读 本文以 Prisma 服务端数据建模为核心系统讲解后端数据库GraphQLPrisma 数据建模SDL完全指南用 GraphQL Schema Definition Language 定义你的数据模型Prisma 数据建模SDL完全指南用 GraphQL Schema Definition Language 定义你的数据模型 导读 本指南以 Prism后端数据库GraphQLPrisma 数据建模完全指南基于 GraphQL SDL 设计数据模型Data ModellingPrisma 数据建模完全指南基于 GraphQL SDL 设计数据模型Data Modelling 导读 本文以 Prisma 1.x 官方参考文档《D后端数据库GraphQL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考