- 后端
- 数据库
- GraphQL
【免费下载链接】prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]
本篇技术指南以 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 数据库 Schema
Prisma 服务的 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 数据库 Schema(Prisma database schema)。它本质上是从你的数据模型推导出的"可执行形态"——数据模型是声明式的类型定义,而 Prisma 数据库 Schema 是面向客户端的完整操作接口。
关于数据模型与 Prisma 数据库 Schema 之间的详细差异,可阅读数据建模(Data Model)章节。完整的数据模型定义语法(
@unique、@default、@relation等指令)参见 02-Service-Configuration 中的相关文档。
高级 API 概念
节点选择(Node selection)
Prisma API 中的许多操作只影响数据库中现有节点的一个子集,很多时候甚至只影响单个节点。这时你需要一种在 API 中"点名"特定节点的机制——大多数情况下通过where参数完成。
节点可以通过任何标注了@unique指令的字段进行选择。这意味着除了系统默认的id字段,任何你声明为@unique的业务字段(如email、slug)都可以作为查询或变更的唯一入口。
考虑下面这个简单的数据模型:
type Post { id: ID! @unique title: String! published: Boolean @default(value: "false") }下面是几个需要节点选择的典型场景。
按email字段检索单个节点(注意:示例中的数据模型需将email声明为@unique):
query { post(where: { email: "hello@graph.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)以及InvalidFirstArgument(3026)、InvalidLastArgument(3027)、InvalidSkipArgument(3028)分别约束了first/last不可同时出现、各分页参数不可为负数等边界条件。
事务性变更(Transactional mutations)
Prisma API 中非批处理的单个变更总是以事务方式执行,即使它包含许多可能跨越多个关系的动作也是如此。这对**嵌套变更(nested mutations)**尤其重要——嵌套变更会在多个类型上执行多次数据库写入。
举个例子:在一个变更中创建User节点和两个将被连接的Post节点,同时把该User节点连接到另外两个已存在的Post节点。如果其中任何一个动作失败(例如违反了@unique字段约束),整个变更都会被回滚。
变更具有事务性,意味着它们是**原子(atomic)且隔离(isolated)**的:
- 原子性:同一嵌套变更中的多个动作要么全部成功,要么全部失败并回滚;
- 隔离性:在同一嵌套变更的两个动作之间,其他变更无法修改数据;且单个动作的结果在整个变更处理完成之前不可被观测。
这为多对象、跨关系写入提供了强一致性的保障,是 Prisma 将"关系数据库事务语义"暴露到 GraphQL 层的关键设计。
级联删除(Cascading deletes)
Prisma 支持为数据模型中的关系配置不同的删除行为。有两种主要的删除行为:
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字段会被置为null(BlogOwner关系中 User 一侧的owner字段为SET_NULL)。
- 所有关联的
- 删除
Comment节点时:- 关联的
Blog节点继续存在,被删除的Comment节点会从它的comments列表中移除; - 关联的
User节点继续存在,被删除的Comment节点会从它的comments列表中移除。
- 关联的
注意删除行为是按关系方向分别声明的:同一个关系(例如
BlogOwner)在User侧和Blog侧可以配置不同的onDelete值,如上例所示。
本仓库的测试代码对级联删除行为进行了系统验证:CascadingDeleteSpec.scala 中大量使用@relation(onDelete: CASCADE)(配合link: INLINE)构造多级嵌套模型,并断言删除父节点后子节点的级联删除结果,可直接作为理解该语义的实测参考。
认证(Authentication)
API Secret
Prisma 服务的 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 Token
API Token 用于对 Prisma API 进行身份认证。API Secret 用于签发一个 JWT(JSON Web Token),该 JWT 需要放在 HTTP 请求的Authorization头中发送给 Prisma API:
Authorization: Bearer __YOUR_API_TOKEN__JWT 是标准化的令牌格式,包含 Header、Payload、Signature 三部分,Signature 由 Secret 签名生成,服务端通过验签来确认令牌的真实性。
使用 Prisma CLI 获取 API Token
获取 API Token 最简单的方式是使用 Prisma CLI 的prisma token命令(命令参考见 07-CLI-Command-Reference):
prisma 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中没有设置secret,CLI 会输出提示信息There is no secret set in the prisma.yml。
令牌的生成逻辑在 cli/packages/prisma-yml/src/PrismaDefinition.ts 的getToken方法中:它使用jwt.sign签发 token,Payload 包含data.service = "serviceName@stageName"以及data.roles = ['admin'],默认有效期expiresIn: '7d'(7 天)。注意这与原文档"未来可能会引入 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 time):
exp,token 的过期时间。 - 服务信息(Service information):
service,服务的名称与 stage(阶段)。
下面是 JWT 的一个示例 Payload:
{ "exp": 1300819380, "service": "my-service@prod" }原文档提示:未来可能会通过引入如
["write:Log", "read:*"]这样的角色概念,支持更细粒度的访问控制。从当前仓库源码看,CLI 生成的 token 已固定携带roles: ['admin']声明(见上文),但服务端AuthImpl.verify目前只校验签名与exp,尚未按角色做授权分发。
在 JavaScript 中生成服务 Token
考虑下面的prisma.yml:
service: 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阶段签发已签名的 JWT:
var 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 errors)
API 返回错误通常说明请求的查询或变更存在问题:你可能不小心拼错了字段,或在查询中遗漏了必填参数。请结合错误信息仔细检查你的输入。
故障排查:常见错误
下面是一个典型的应用错误示例。
认证问题 —— 权限不足 / 无效 token(Invalid 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 的上限;3002DataItemDoesNotExist:where选择的唯一字段值不存在;3010UniqueConstraintViolation:唯一约束将被违反;3014InvalidConnectionArguments:first与last同时传入;3026/3027/3028:first/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)与 CLI(cli)源码的基础。进一步可阅读同目录下的 Queries 参考、Mutations 参考 与 Subscriptions 参考 获取各操作类型的完整参数说明。
- 后端
- 数据库
- GraphQL
【免费下载链接】prisma1
💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]
相关推荐
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),仅供参考