Prisma API 核心概念深度解析:数据模型、节点选择、事务与认证机制
后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载本篇技术指南以 Prisma 1.12 官方参考文档《Concepts》为骨架系统讲解 Prisma API 的基础设计理念与高级机制从API 围绕数据模型自动生成这一核心思想出发依次展开节点选择Node selection、批处理操作、Relay 风格连接Connections、事务性变更、级联删除再到 API Secret / API Token 认证体系与错误处理。读者学完后将能准确理解 Prisma 的where选择器、updateMany/deleteMany批处理语义、onDelete删除行为并能在自己的服务中正确配置认证、签发与验证 JWT、排查常见 API 错误。文中所有机制均结合本仓库prisma1的服务端与 CLI 源码进行印证可在 03-Prisma-API 系列文档与 server / cli 源码中进一步溯源。数据模型与 Prisma 数据库 SchemaPrisma 服务的 API 完全围绕其**数据模型Data Model**构建API 会根据与 Prisma 服务关联的数据模型自动生成对应的 GraphQL 操作。这意味着你无需手写任何 resolver只需声明类型与关系即可获得完整的读写接口。Prisma API 中暴露的每一个操作都与数据模型中的某个model模型或relation关系对应Queries查询参见 03-Queries.md查询某个模型的单个或多个节点跨关系查询节点查询跨关系的聚合数据Mutations变更参见 04-Mutations.md创建、更新、upsert 和删除某个模型的节点跨关系创建、连接connect、断开disconnect、更新和 upsert 节点批量更新或删除某个模型的节点Subscriptions订阅参见 05-Subscriptions.md在节点被创建、更新或删除时获得通知定义 Prisma API 中可用 GraphQL 操作的实际 [GraphQL schema] 也被称为Prisma 数据库 SchemaPrisma database schema。它本质上是从你的数据模型推导出的可执行形态——数据模型是声明式的类型定义而 Prisma 数据库 Schema 是面向客户端的完整操作接口。关于数据模型与 Prisma 数据库 Schema 之间的详细差异可阅读数据建模Data Model章节。完整的数据模型定义语法unique、default、relation等指令参见 02-Service-Configuration 中的相关文档。高级 API 概念节点选择Node selectionPrisma API 中的许多操作只影响数据库中现有节点的一个子集很多时候甚至只影响单个节点。这时你需要一种在 API 中点名特定节点的机制——大多数情况下通过where参数完成。节点可以通过任何标注了unique指令的字段进行选择。这意味着除了系统默认的id字段任何你声明为unique的业务字段如email、slug都可以作为查询或变更的唯一入口。考虑下面这个简单的数据模型type Post { id: ID! unique title: String! published: Boolean default(value: false) }下面是几个需要节点选择的典型场景。按email字段检索单个节点注意示例中的数据模型需将email声明为uniquequery { post(where: { email: hellograph.cool }) { id } }更新单个节点的title字段mutation { updatePost( where: { id: ohco0iewee6eizidohwigheif } data: { title: GraphQL is awesome } ) { id } }一次更新多个节点的published字段使用id_in过滤器也参见下文批处理操作mutation { updatePost( where: { id_in: [ohco0iewee6eizidohwigheif, phah4ooqueengij0kan4sahlo, chae8keizohmiothuewuvahpa] } data: { published: true } ) { count } }从服务端实现看节点选择在 API 连接器层被抽象为NodeSelector本仓库的 Errors.scala 中定义了NodeNotFoundForWhereError错误码 3039消息为No Node for the model ... with value ... for ... field ... found与NullProvidedForWhereError错误码 3040它们专门对应where选择器无法命中节点或选择器为空的场景可见where的唯一性校验发生在请求解析阶段。批处理操作Batch operations节点选择概念的一个典型应用就是批处理操作。批量更新与批量删除针对大规模节点变更场景做了优化与单节点变更返回完整节点信息不同这类变更只返回受影响节点的数量。例如updateManyPosts和deleteManyPosts这两个变更接受where参数来选择目标节点并通过count字段返回受影响节点数见上面一次更新多个节点的示例。⚠️重要提示批处理变更不会触发任何订阅subscription事件如果你的业务依赖订阅通知来响应数据变更请务必注意updateMany*/deleteMany*不会向订阅端推送消息需要自行补偿处理。连接Connections与直接返回节点列表的简单对象查询不同连接Connection查询基于 Relay Connection 模型。除了分页信息之外连接还提供聚合aggregation等高级能力。例如posts查询允许你选择特定的Post节点、按字段排序并分页而postsConnection查询还能让你统计所有未发布unpublished帖子的数量query { postsConnection { # aggregate 允许执行常见的聚合操作 aggregate { count } edges { # 每个 node 引用一个单独的 Post 元素 node { title } } } }连接查询的edges.node结构、aggregate聚合块与 Relay 分页参数first、last、before、after共同构成了面向客户端的分页与统计接口。仓库中 Errors.scala 的InvalidConnectionArguments错误码 3014以及InvalidFirstArgument3026、InvalidLastArgument3027、InvalidSkipArgument3028分别约束了first/last不可同时出现、各分页参数不可为负数等边界条件。事务性变更Transactional mutationsPrisma API 中非批处理的单个变更总是以事务方式执行即使它包含许多可能跨越多个关系的动作也是如此。这对**嵌套变更nested mutations**尤其重要——嵌套变更会在多个类型上执行多次数据库写入。举个例子在一个变更中创建User节点和两个将被连接的Post节点同时把该User节点连接到另外两个已存在的Post节点。如果其中任何一个动作失败例如违反了unique字段约束整个变更都会被回滚。变更具有事务性意味着它们是**原子atomic且隔离isolated**的原子性同一嵌套变更中的多个动作要么全部成功要么全部失败并回滚隔离性在同一嵌套变更的两个动作之间其他变更无法修改数据且单个动作的结果在整个变更处理完成之前不可被观测。这为多对象、跨关系写入提供了强一致性的保障是 Prisma 将关系数据库事务语义暴露到 GraphQL 层的关键设计。级联删除Cascading deletesPrisma 支持为数据模型中的关系配置不同的删除行为。有两种主要的删除行为CASCADE级联删除当某个与一个或多个其他节点存在关系的节点被删除时这些关联节点也会一并被删除。SET_NULL置空当某个与一个或多个其他节点存在关系的节点被删除时指向被删除节点的字段会被设为null。具体到某个关系使用哪种行为由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节点会被级联删除CommentAuthor关系为CASCADE关联的Blog节点会被级联删除BlogOwner关系指向 User 的一侧为CASCADE。删除Blog节点时所有关联的Comment节点会被级联删除Comments关系指向 Blog 的一侧为CASCADE关联的User节点的blog字段会被置为nullBlogOwner关系中 User 一侧的owner字段为SET_NULL。删除Comment节点时关联的Blog节点继续存在被删除的Comment节点会从它的comments列表中移除关联的User节点继续存在被删除的Comment节点会从它的comments列表中移除。注意删除行为是按关系方向分别声明的同一个关系例如BlogOwner在User侧和Blog侧可以配置不同的onDelete值如上例所示。本仓库的测试代码对级联删除行为进行了系统验证CascadingDeleteSpec.scala 中大量使用relation(onDelete: CASCADE)配合link: INLINE构造多级嵌套模型并断言删除父节点后子节点的级联删除结果可直接作为理解该语义的实测参考。认证AuthenticationAPI SecretPrisma 服务的 GraphQL API 通常受API Secret保护你需要在prisma.yml中通过secret属性指定它参见 prisma.yml 参考文档。下面是一个指定了secret的prisma.yml示例endpoint: http://localhost:4466/myapi/dev datamodel: datamodel.graphql secret: mysecret123 # your API secretsecret是认证体系的根密钥所有访问该服务 API 的请求都必须携带由该secret签名的令牌。从服务端实现看认证是可选的——本仓库 Auth.scala 的verify方法中当secrets为空向量即未配置 secret时直接返回AuthSuccess只有配置了 secret 的服务才要求校验请求头。API TokenAPI Token 用于对 Prisma API 进行身份认证。API Secret 用于签发一个 JWTJSON Web Token该 JWT 需要放在 HTTP 请求的Authorization头中发送给 Prisma APIAuthorization: Bearer __YOUR_API_TOKEN__JWT 是标准化的令牌格式包含 Header、Payload、Signature 三部分Signature 由 Secret 签名生成服务端通过验签来确认令牌的真实性。使用 Prisma CLI 获取 API Token获取 API Token 最简单的方式是使用 Prisma CLI 的prisma token命令命令参考见 07-CLI-Command-Referenceprisma token在包含prisma.yml的目录下运行该命令时CLI 会读取prisma.yml中的secret属性并生成对应的 JWT 打印到终端。从源码看CLI 实现位于 cli/packages/prisma-cli-core/src/commands/token/token.ts它还提供了几个实用参数--copy/-c将 token 复制到剪贴板而不是打印源码中通过clipboardy实现--env-file/-e指定.env文件路径以注入环境变量--project/-p指定 Prisma 定义文件prisma.yml的路径。如果prisma.yml中没有设置secretCLI 会输出提示信息There is no secret set in the prisma.yml。令牌的生成逻辑在 cli/packages/prisma-yml/src/PrismaDefinition.ts 的getToken方法中它使用jwt.sign签发 tokenPayload 包含data.service serviceNamestageName以及data.roles [admin]默认有效期expiresIn: 7d7 天。注意这与原文档未来可能会引入 roles 角色概念的描述不同——在当前仓库的 CLI 实现中roles: [admin]已经作为固定声明被写入每个 CLI 生成的 token。在 GraphQL Playground 中认证拿到 API Token 后就可以用它来认证 API 请求例如通过 GraphQL Playground 调用 API打开你的 Prisma API 对应的 Playground点击左下角的HTTP HEADERS区域将你的 API Token 作为Authorization字段的值粘贴进去{ Authorization: Bearer __YOUR_API_TOKEN__ }使用真实 token 时可能看起来像这样{ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJibG9nckBkZXYiLCJyb2xlcyI6WyJhZG1pbiJdfSwiaWF0IjoxNTE4NzE2NjA4LCJleHAiOjE1MTkzMjE0MDh9.zqBh_Oo4RmV4j3UQeVDYqJDxV-YHQiOR-XIlhjbWejw }JWT Claims声明JWT 必须包含以下不同的claims过期时间Expiration timeexptoken 的过期时间。服务信息Service informationservice服务的名称与 stage阶段。下面是 JWT 的一个示例 Payload{ exp: 1300819380, service: my-serviceprod }原文档提示未来可能会通过引入如[write:Log, read:*]这样的角色概念支持更细粒度的访问控制。从当前仓库源码看CLI 生成的 token 已固定携带roles: [admin]声明见上文但服务端AuthImpl.verify目前只校验签名与exp尚未按角色做授权分发。在 JavaScript 中生成服务 Token考虑下面的prisma.ymlservice: my-service stage: ${env:PRISMA_STAGE} cluster: ${env:PRISMA_CLUSTER} datamodel: database/datamodel.graphql secret: ${env:PRISMA_SECRET}注意此示例使用了prisma.yml内的环境变量${env:...}语法相关说明见 02-Service-Configuration 中的环境变量章节。一个 Node 服务可以基于jsonwebtoken库为服务my-service的PRISMA_STAGE阶段签发已签名的 JWTvar jwt require(jsonwebtoken) jwt.sign( { data: { service: my-service process.env.PRISMA_STAGE, }, }, process.env.PRISMA_SECRET, { expiresIn: 1h, } )注意该示例将service声明放在data嵌套对象中这与 CLI 的getToken实现结构一致data.service与data.roles。服务端验证时会对data.service进行匹配校验。JWT 验证对发往 Prisma 服务的请求JWT 的以下属性会被逐一验证签名必须使用为该服务配置的 secret 进行签名expclaim必须存在且其时间值在未来即 token 未过期serviceclaim必须存在且其中的服务名与 stage 与当前请求匹配。服务端的具体实现在 server/libs/auth/src/main/scala/com/prisma/auth/Auth.scala使用HS256算法JwtAlgorithm.HS256加解密verify时启用JwtOptions(signature true, expiration true)即强制校验签名与过期时间从Authorization头中剥离Bearer前缀后解码并遍历所有配置的 secrets支持多 secret 轮换当secrets为空时直接放行未配置 secret 的服务不要求认证createToken内部默认生成exp为当前时间 86400 秒1 天、并带notBefore当前时间的 claim。订阅端口的认证同样遵循该机制SubscriptionsAuthSpec.scala 测试中用正确 secret 签发的 token 可正常建立订阅而用other-secret签发的 token 会被拒绝直接印证了必须使用服务配置的 secret 签名这一验证规则。错误处理Error handling当某个查询或变更发生错误时响应会包含一个errors属性其中携带错误code、错误message等详细信息。API 错误分为两类应用错误Application errors通常表示你的请求本身无效例如参数拼写错误、缺少必填参数、认证失败。内部服务器错误Internal server errors通常表示 Prisma 服务内部发生了意外情况需要查看服务日志定位。注意errors字段的行为遵循官方 GraphQL 规范中关于错误处理的部分。应用错误Application errorsAPI 返回错误通常说明请求的查询或变更存在问题你可能不小心拼错了字段或在查询中遗漏了必填参数。请结合错误信息仔细检查你的输入。故障排查常见错误下面是一个典型的应用错误示例。认证问题 —— 权限不足 / 无效 tokenInvalid token{ errors: [ { code: 3015, requestId: api:api:cjc3kda1l000h0179mvzirggl, message: Your token is invalid. It might have expired or you might be using a token from a different project. } ] }遇到该错误时请检查你提供的 token 是否尚未过期token 是否使用prisma.yml中列出的 secret 签名即是否来自正确的服务/项目。在服务端错误码 3015 对应 Errors.scala 中的InvalidToken错误类其消息原文与文档一致。结合 Auth.scala 的验证逻辑可以推断该错误通常在验签失败secret 不匹配或exp校验不通过token 过期时抛出。除 3015 外同一文件 Errors.scala 还定义了丰富的应用错误码可作为排查参考例如1000TimeoutExceeded查询处理超时2041TooManyNodesRequested单次查询请求节点数超过 1000 的上限3002DataItemDoesNotExistwhere选择的唯一字段值不存在3010UniqueConstraintViolation唯一约束将被违反3014InvalidConnectionArgumentsfirst与last同时传入3026/3027/3028first/last/skip参数为负数3032RelationIsRequired变更会违反必填关系约束3042RequiredRelationWouldBeViolated变更将破坏必填关系。内部服务器错误Internal server errors当收到内部服务器错误时请查阅服务日志获取更多信息。对于本地集群可以使用prisma logs命令查看命令参考见 07-CLI-Command-Reference。小结Prisma API 的设计可以概括为一次数据建模全量操作自动生成数据模型驱动 API 形态查询、变更、订阅where选择器与 Relay 连接模型提供了灵活的节点定位与分页聚合能力事务性变更保证了多对象写入的原子性与隔离性onDelete让关系删除行为显式可控认证层则由 API Secret 签发 JWT 完成令牌化鉴权。理解这些核心概念是高效使用 Prisma 服务、排查 3xxx 系列错误码、以及阅读本仓库服务端server与 CLIcli源码的基础。进一步可阅读同目录下的 Queries 参考、Mutations 参考 与 Subscriptions 参考 获取各操作类型的完整参数说明。赞分享后端数据库GraphQL【免费下载链接】prisma1 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL MongoDB) [deprecated]项目地址https://gitcode.com/gh_mirrors/pr/prisma1点击查看免费下载相关推荐ADB WebKit未来更新预告Root功能修复与新特性抢先看ADB WebKit未来更新预告Root功能修复与新特性抢先看 ADB WebKit是一款功能强大的ADB浏览器管理工具让用户能够通过浏览器轻松访问和管理A后端数据库GraphQLPrisma API 核心概念深度解析节点选择、事务、JWT 认证与错误处理Prisma API 核心概念深度解析节点选择、事务、JWT 认证与错误处理 导读 本文以 Prisma 1.x 官方参考文档中「Concepts」一章为核心后端数据库GraphQLPrisma 1.x Prisma API 核心概念全解析节点选择、批量操作、事务性与 JWT 认证Prisma 1.x Prisma API 核心概念全解析节点选择、批量操作、事务性与 JWT 认证 本指南以 Prisma 1.x 文档库中 Concept后端数据库GraphQL创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考