尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Prisma 数据建模指南:用 GraphQL SDL 设计数据模型(Data Modelling)

Prisma 数据建模指南:用 GraphQL SDL 设计数据模型(Data Modelling) 后端数据库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数据模型文件并通过prisma.yml的datamodel属性将其接入 Prisma 服务。你将掌握对象类型、标量字段、关系、指令unique、default、relation、rename、系统字段与命名规范等完整建模要素并理解数据模型与最终生成的 Prisma database schema 之间的关系——这是后续使用 Prisma GraphQL API查询、变更、订阅的基石。概览SDL 与datamodel属性Prisma 使用 GraphQL 的 Schema Definition LanguageSDL进行数据建模。数据模型写在一个或多个.graphql文件中是 Prisma 在底层生成真实数据库 schema 的基础。如果只使用单个文件存放类型定义这个文件通常命名为datamodel.graphql。数据模型文件需要在prisma.yml的datamodel属性下指定。例如可以指定多个文件datamodel: - types.graphql - enums.graphql如果只有一个文件定义数据模型可以简写为datamodel: datamodel.graphql从仓库源码看prisma-yml包中的getTypesString方法见 cli/packages/prisma-yml/src/PrismaDefinition.ts正是这样处理datamodel属性的当属性是数组时逐个遍历是字符串时包装成单元素数组再读取每个文件的内容并拼接为一份完整的 SDL 字符串。若文件不存在会抛出错误The types definition file ... could not be found。另外deploy命令会先检查prisma.yml中是否缺少datamodel属性见 cli/packages/prisma-cli-core/src/commands/deploy/deploy.ts。数据模型是 Prisma 服务 GraphQL API 的基础。基于数据模型Prisma 会生成一个强大的 GraphQL schema称为Prisma database schema它为数据模型中的每个类型定义 CRUD 操作。补充说明GraphQL schema 定义了 GraphQL API 的操作本质上是使用 SDL 编写的类型集合SDL 还支持接口、枚举、联合类型等基本类型。一个 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三个类型会被映射为数据库中的表。User与Tweet之间存在双向关系。Tweet到Location之间存在单向关系。除User上的name字段外数据模型中所有字段都是必填的由类型后的!表示。id、createdAt和updatedAt字段由 Prisma 托管在暴露的 GraphQL API 中只读不能通过变更修改。创建和更新数据模型就像写一个文本文件一样简单。数据模型就绪后运行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数据模型的构建块数据模型由以下几种构建块组成类型Types由多个字段fields组成用于将相似实体分组。数据模型中的每个类型都会被映射到数据库并为 GraphQL schema 添加 CRUD 操作。关系Relations描述类型之间的关联。接口Interfaces抽象类型包含一组类型必须实现才能实现该接口的字段。目前接口不能由用户自定义已有一个关于高级接口支持的待实现功能请求。特殊的指令directives覆盖类型约束、级联删除等不同用例。Prisma database schema 与数据模型的区别刚接触 GraphQL 和 Prisma 时面对多个.graphql文件容易混淆。关键在于理解每个文件的作用。一般来说.graphql文件可以包含以下任一种内容GraphQL 操作即查询queries、变更mutations或订阅subscriptionsSDL 中的 GraphQL 类型定义在区分 Prisma database schema 与数据模型的语境下只有后者相关。注意并非所有属于后者的.graphql文件天然就是有效的GraphQL schema。如上面信息框所述GraphQL schema 的特征在于除了 API 所需的其它类型外还包含Query、Mutation和Subscription三个根类型。按这个定义数据模型实际上并不是 GraphQL schema——尽管它是用 SDL 编写的.graphql文件。它缺少根类型因此实际上并不定义 API 操作。Prisma 只是把数据模型当作一种方便的工具让你描述数据的形态。如上所述Prisma 随后会生成一个包含Query、Mutation和Subscription根类型的实际 GraphQL schema。这个 schema 通常作为prisma.graphql存放在项目中称为Prisma database schema。注意千万不要手动修改这个文件。以下面这个非常简单的数据模型为例datamodel.graphqltype User { id: ID! unique name: String! }将这个数据模型部署到 Prisma 服务后Prisma 会生成下面的 Prisma database schema定义服务的 GraphQL APIprisma.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 的简化版本。例如users查询支持where、orderBy、skip、after、before、first、last等参数分别对应过滤、排序、分页含基于游标的前后分页能力。如果你已经在研究如何基于 Prisma 构建自己的 GraphQL 服务器可能还遇到过另一个.graphql文件即应用 schemaapplication schema。这是另一个正式的 GraphQL schema包含Query、Mutation、Subscription根类型定义了对客户端应用暴露的 API。它把底层的 Prisma GraphQL API 当作“查询引擎”实际执行针对数据库的查询、变更和订阅。基于 Prisma 的 GraphQL 服务器通常有两个 GraphQL API可以理解为服务的两层应用层由应用 schema 定义在这里实现业务逻辑、认证、与第三方服务集成等数据库层由 Prisma database service 定义对象类型Object types对象类型简称类型定义数据模型中某一块具体数据的结构用于表示应用领域中的实体entities。如果你熟悉 SQL 数据库可以把对象类型理解为关系型数据库中表table的 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从源码看数据模型的解析由prisma-datamodel包完成DefaultParser.parseFromSchemaString先调用graphql包的parse生成 AST再遍历ObjectTypeDefinition与EnumTypeDefinition两种定义见 cli/packages/prisma-datamodel/src/datamodel/parser/parser.ts每个字段通过parseField提取名称、类型、是否列表、是否必填、默认值、唯一性、关系名等属性parser.ts。仓库测试数据中也包含大量带unique、default、relation指令的类型定义见 cli/packages/prisma-datamodel/tests/clone/clone.ts。类型生成的 API 操作数据模型中的类型会影响 Prisma GraphQL API 中可用的操作。对于每个类型queries 允许你获取该类型的一个或多个节点mutations 允许你创建、更新或删除该类型的节点subscriptions 允许你获得该类型节点变更的通知即节点被创建或已有节点被更新或删除字段Fields字段是类型的构建块赋予节点形状。每个字段通过名称引用要么是标量scalar字段要么是关系relation字段。标量类型Scalar types仓库中prisma-datamodel包定义了完整的标量类型清单见 cli/packages/prisma-datamodel/src/datamodel/scalar.tsString、Int、Float、Boolean、Long、DateTime、ID、UUID、Json并通过isTypeIdentifier判断某个类型名是否为内置标量。下面逐一说明。StringString存放文本。适合用户名、博客文章内容等以文本形式表示的数据。注意在共享 demo 集群上String 值目前限制为 256KB。在其他集群上可以通过集群配置提高该限制。在查询或变更中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枚举在服务范围内定义。与 Boolean 类似Enum 的值是一组预定义值中的一个。区别在于你可以自定义可能的值。例如可以创建一个可能值为COMPACT、WIDE和COVER的 Enum指定文章的排版格式。注意Enum 值最多 191 个字符。在查询或变更中Enum 字段不需要任何括字符只能使用你为枚举定义的值enum: COMPACT、enum: WIDE。在解析层面枚举类型由parseEnumType处理parser.ts每个枚举值被解析为名称属性有效的字段类型标记为isEnum: true。Json有时需要为松散结构的数据存储任意 Json 值。Json类型确保存储的是合法 Json并返回解析后的 Json 对象/数组而非字符串。注意Json 值在共享 demo 集群上目前限制为 256KB。其他集群可通过集群配置提高。在查询或变更中Json 字段必须用双引号括起来特殊字符需要转义json: {\int\: 1, \string\: \value\}。IDID 值是基于 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类型上的email字段假设每个User都应有一个全局唯一的电子邮件地址。请注意String 字段只有前 191 个字符参与唯一性判定且唯一性检查不区分大小写。如果两个字符串的前 191 个字符相同或仅大小写不同则无法同时存储。将字段标记为唯一只需在字段后追加unique指令type User { email: String! unique age: Int! }对于每个标注了unique的字段你都可以通过为该字段提供值来查询对应节点。例如对于上面的数据模型现在可以按email地址检索特定的User节点query { user(where: { email: alicegraph.cool }) { age } }从源码看unique是内置保留指令DirectiveKeys.isUnique unique见 cli/packages/prisma-datamodel/src/datamodel/directives.ts。解析时isUniqe检查字段是否带unique指令且id字段自动视为唯一isUnique isId || this.isUniqe(field)见 parser.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值也总是需要用双引号提供。源码中getDefaultValue会读取字段上default指令的value参数parser.ts这也印证了“所有标量类型的value参数都是字符串”这一约定。系统字段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防碰撞唯一标识方案注意数据库 schema 中所有对象类型都会实现Node接口。Node接口如下interface Node { id: ID! unique }从源码看isReservedReadOnlyField将id、createdAt、updatedAt字段统一标记为只读parser.ts且关系型解析器RelationalParser在识别这些保留字段时同时支持id、createdAt、updatedAt指令与按名称回退两种方式见 cli/packages/prisma-datamodel/src/datamodel/parser/relationalParser.ts 与 cli/packages/prisma-datamodel/src/datamodel/legacyFields.ts。系统字段createdAt和updatedAt数据模型还提供了两个可以添加到类型中的特殊字段createdAt: DateTime!存储该对象类型的节点被创建的确切日期和时间。updatedAt: DateTime!存储该对象类型的节点被最后更新的确切日期和时间。如果希望类型暴露这些字段只需将它们添加到类型定义中例如type User { id: ID! unique createdAt: DateTime! updatedAt: DateTime! }字段生成的 API 操作数据模型中的字段会影响可用的查询参数。迁移标量字段的值可以使用updateManyXs变更来迁移所有节点或特定子集的标量字段值。mutation { # 将所有没有 email 地址的用户的 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。添加必填字段需要以下步骤将该字段添加为可选optional使用updateManyXs将字段值从null迁移为非空值然后将字段标记为必填并正常部署关系Relations关系定义两个类型之间连接connection的语义。两个通过关系连接的类型通过关系字段相连。当关系可能产生歧义时关系字段需要用relation指令标注来消除歧义。关系也可以连接类型自身称为自关联self-relation。必填关系Required relations对于to-one关系字段可以配置它是必填还是可选。必填标志在 GraphQL 中作为该字段永远不为null的契约。因此用户地址字段的类型是Address或Address!。包含必填to-one关系字段的类型的节点只能通过嵌套变更nested 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列表中被移除。从源码看关系解析的关键逻辑在resolveRelationsparser.ts首先把字段类型从字符串解析为对应的类型对象然后通过relationName连接显式命名的关系两端若两端类型不匹配会抛出Relation type mismatch错误最后对未命名且不歧义的关系按类型匹配自动连接若一个类型对同一目标类型有多个字段如存在多个同类型关系字段则跳过自动连接——这正是relation(name:)用于消除歧义的实现基础。仓库测试cli/packages/prisma-datamodel/tests/clone/clone.ts中SentMessages/ReceivedMessages这类命名关系也是同样的写法。省略relation指令在最简单的情况下如果两个类型之间的关系没有歧义且应使用默认删除行为SET_NULL则对应的关系字段不必标注relation指令。下面定义了User和Story类型之间的双向one-to-many关系。由于未提供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字段。使用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列表中移除。关系生成的 API 操作schema 中包含的关系会影响 GraphQL API 中可用的操作。对于每个关系关系查询relation queries 允许跨类型查询数据或按关系聚合查询注意也可以使用 Relay 的 connection 模型嵌套变更nested mutations 允许跨类型创建、连接、更新、upsert 和删除节点关系订阅relation subscriptions 允许获得关系变更的通知GraphQL 指令Directives指令用于在数据模型中提供额外信息。形式为name(argument: value)无参数时简写为name。数据模型指令数据模型指令描述 GraphQL schema 中类型或字段的附加信息。唯一标量字段unique指令将标量字段标记为唯一。唯一字段会在底层数据库中应用唯一索引。# User 类型有一个唯一的 email 字段 type User { email: String unique }更多关于unique指令的信息见上文。关系字段指令relation(name: String, onDelete: ON_DELETE! NO_ACTION)可以附加到关系字段上。更多信息见上文。标量字段的默认值指令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) }在源码层面prisma-datamodel维护了一份保留指令清单——default、unique、relation、id、createdAt、updatedAt、db、index、sequence、embedded等见 directives.ts 与getReservedDirectiveNamesparser.ts。解析字段时这些内置指令被识别为相应属性其余非保留指令则通过parseDirectives原样保留为通用指令列表。临时指令Temporary directives临时指令用于执行一次性迁移操作。部署包含临时指令的服务后需要手动将其从类型定义文件中移除。重命名类型或字段临时指令rename(oldName: String!)用于重命名类型或字段。# 将 Post 类型重命名为 Story将其 text 字段重命名为 content type Story rename(oldName: Post) { content: String rename(oldName: text) }警告如果不使用重命名指令Prisma 会先删除旧类型和旧字段再创建新的导致数据丢失命名规范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 的信息可查看相关功能请求。联合类型Union types“联合类型与接口非常相似但它们不需要指定类型之间的公共字段。”——引自官方 GraphQL 文档注意要了解更多关于联合类型何时以及如何引入 Prisma 的信息可查看相关功能请求。小结围绕 Prisma 的数据建模本文完整梳理了从prisma.yml配置datamodel属性、编写 SDL 类型定义到理解数据模型与 Prisma database schema 的区别、掌握对象类型与字段体系含全部标量类型、类型修饰符、约束与系统字段、设计单向/双向/自关联关系含relation的name与onDelete语义、使用内置与临时指令unique、default、rename以及遵循命名规范的完整知识链路。想要深入实践可以在仓库中找到对应的实现与测试数据模型解析的核心在 cli/packages/prisma-datamodel/src/datamodel/parser/parser.ts指令键定义在 cli/packages/prisma-datamodel/src/datamodel/directives.ts标量类型清单在 cli/packages/prisma-datamodel/src/datamodel/scalar.tsprisma.yml的解析在 cli/packages/prisma-yml/src/PrismaDefinition.ts而prisma deploy的入口在 cli/packages/prisma-cli-core/src/commands/deploy/deploy.ts。结合这些源码阅读可以更深入地理解本文涉及的每个建模概念在引擎层面的落地方式。赞分享后端数据库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 1.x 官方参考文档《D后端数据库GraphQLPrisma 数据建模SDL完全指南用 GraphQL SDL 设计数据模型、字段约束与关系Prisma 数据建模SDL完全指南用 GraphQL SDL 设计数据模型、字段约束与关系 导读 本文是 Prisma 1.x 数据建模Data Mo后端数据库GraphQLPrisma 数据建模SDL完全指南用 GraphQL Schema Definition Language 定义你的数据模型Prisma 数据建模SDL完全指南用 GraphQL Schema Definition Language 定义你的数据模型 导读 本指南以 Prism后端数据库GraphQL上一篇Nuxt Content 组件插槽(Slot)机制深度解析下一篇mcp-use项目快速入门指南构建智能代理的完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表