Prisma API 概览:探索 Prisma 服务自动生成的 GraphQL CRUD API
【免费下载链接】prisma1💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]项目地址: https://gitcode.com/gh_mirrors/pr/prisma1
导读
本指南讲解 Prisma 1.x 中Prisma API的核心概念与使用方法。Prisma API 是基于已部署的 数据模型 自动生成的 GraphQL API,为数据模型中的每个类型提供 CRUD 操作,并支持数据库事件的实时订阅。阅读完本文,你将掌握 Prisma API 的组成(查询、变更、订阅)、如何用 GraphQL Playground 与服务端点交互、API 的认证机制(API secret / JWT token)以及常见错误的排查思路。
提示:本文基于仓库中 docs/1.12/04-Reference/03-Prisma-API 的 Overview 章节展开,并结合同目录的 Concepts、Queries、Mutations、Subscriptions 章节,以及仓库源码中的实现细节进行深化。
什么是 Prisma API?
一个 Prisma 服务会暴露一个GraphQL API,该 API 是基于服务已部署的数据模型(data model)自动生成的,通常被称为Prisma API。
Prisma API 为数据模型中的每个类型自动生成 CRUD 操作,主要分为三类能力:
- Queries(查询):查询某个模型的单个或多个节点、跨关系查询数据、跨关系聚合数据
- Mutations(变更):创建、更新、upsert 和删除某个模型的节点;跨关系创建、连接、断开、更新和 upsert 节点;批量更新或删除节点
- Subscriptions(订阅):在节点被创建、更新或删除时获得实时通知
从实现角度看,Prisma API 的实际 GraphQL schema 被称为Prisma database schema,它由 Prisma 服务端根据数据模型动态构建。在仓库的服务端源码中,这一构建逻辑由 server/servers/api/src/main/scala/com/prisma/api/schema/SchemaBuilder.scala 及其实现类负责,并通过 CachedSchemaBuilder 对生成的 schema 做缓存,以提升重复请求的性能。
Prisma API 中暴露的每一个操作都与数据模型中的某个**模型(model)或关系(relation)**相关联:
| 操作类别 | 具体能力 |
|---|---|
| Queries | 查询某个模型的单个或多个节点、跨关系查询、跨关系聚合 |
| Mutations | 创建、更新、upsert、删除节点;跨关系 create/connect/disconnect/update/upsert;批量更新或删除 |
| Subscriptions | 节点被 created / updated / deleted 时获得通知 |
探索 Prisma API
GraphQL Playground 是探索 Prisma API 的最佳工具,你可以用它来执行 GraphQL 查询、变更和订阅,直观地查看 schema 结构、字段类型和可用参数。
打开服务对应 Playground 有两种方式:
- 命令行方式:在服务的工作目录下运行
prisma playground命令; - 浏览器方式:把服务的 HTTP endpoint 粘贴到浏览器地址栏中打开。
prisma playground命令的底层实现
在仓库源码 cli/packages/prisma-cli-core/src/commands/playground/index.ts 中可以看到该命令的完整实现逻辑:
- 命令通过
definition.load读取服务的prisma.yml定义,确定当前stage与所在cluster; - 若
prisma.yml中配置了endpoint,则直接使用;否则通过cluster.getApiEndpoint(service, stage, workspace)动态计算 API 端点; - 默认在本地
3000端口启动一个 express 服务器,将/playground路由交给graphql-playground-middleware-express渲染,并将/graphql路由通过express-request-proxy代理到真实的 Prisma API 端点; - 启动后自动打开浏览器访问
http://localhost:3000/playground。
该命令支持若干 flag,方便在不同场景下使用:
| Flag | 简写 | 说明 |
|---|---|---|
--web | -w | 强制打开 Web 版 Playground |
--env-file | -e | 指定注入环境变量的.env文件路径 |
--project | -p | 指定 Prisma 定义文件(prisma.yml)的路径 |
--server-only | -s | 只启动服务器,不自动打开浏览器 |
--port | -p | 指定 Web 版 Playground 的端口(隐含--web) |
Prisma API 核心概念速览
在深入使用 Prisma API 之前,先了解几个贯穿查询、变更、订阅三大模块的核心概念。详情见 Concepts 章节。
节点选择(Node selection)
Prisma API 中的许多操作只影响数据库中的部分节点,甚至只影响单个节点。此时需要通过where参数来指定目标节点。节点可以通过任意标注了@unique指令的字段来选中。
例如对如下数据模型:
type Post { id: ID! @unique title: String! published: Boolean @default(value: "false") }按唯一字段检索单个节点:
query { post(where: { email: "hello@graph.cool" }) { id } }按id更新单个节点的title:
mutation { updatePost( where: { id: "ohco0iewee6eizidohwigheif" } data: { title: "GraphQL is awesome" } ) { id } }批量更新多个节点(id_in接收一个 id 列表):
mutation { updatePost( where: { id_in: ["ohco0iewee6eizidohwigheif", "phah4ooqueengij0kan4sahlo", "chae8keizohmiothuewuvahpa"] } data: { published: true } ) { count } }批量操作(Batch operations)
节点选择的一个典型应用是批量操作。批量更新或删除针对大量节点做了优化,因此这类 mutation 只返回受影响节点的数量(count),而不返回节点的完整信息。例如updateManyPosts和deleteManyPosts都通过where选择节点,并通过count字段返回受影响数量(见上例)。
⚠️注意:批量 mutation不会触发任何 subscription 事件!
连接查询(Connections)
与直接返回节点列表的简单对象查询不同,连接查询基于 Relay Connection 模型,除了分页信息外,还提供**聚合(aggregation)**等高级特性。
例如posts查询可以按字段排序、分页选取Post节点,而postsConnection查询还可以统计所有未发布的Post数量:
query { postsConnection { # `aggregate` 允许执行常用的聚合操作 aggregate { count } edges { # 每个 `node` 引用一个 `Post` 元素 node { title } } } }事务性变更(Transactional mutations)
Prisma API 中非批量的单次 mutation 总是以事务方式执行,即使它包含跨多个关系的大量操作(例如嵌套变更在多个类型上执行多次数据库写入)。
典型例子:在一次 mutation 中创建一个User节点、两个新的Post节点并连接它们,同时把该User连接到另外两个已存在的Post节点。如果其中任何一步失败(例如违反了@unique约束),整个 mutation 会回滚。
这些 mutation 是事务性的,即具备原子性和隔离性:在同一个嵌套 mutation 的两个独立动作之间,不会有其他 mutation 改变数据;单个动作的结果在整个 mutation 处理完成之前不可见。
级联删除(Cascading deletes)
Prisma 支持为数据模型中的关系配置不同的删除行为,通过@relation指令的onDelete参数指定。有两种主要行为:
CASCADE:当一个节点被删除时,与之关联的节点也会被删除SET_NULL:当一个节点被删除时,指向该节点的字段被置为null
考虑下面的数据模型:
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列表中移除
- 相关的
Prisma API 认证:API secret 与 API token
Prisma 服务的 GraphQL API 通常受API secret保护,即prisma.yml中的secret属性。
示例prisma.yml:
endpoint: http://localhost:4466/myapi/dev datamodel: datamodel.graphql secret: mysecret123 # 你的 API secretAPI token用于对 Prisma API 的请求进行认证。API secret 用于签发 JWT,该 JWT 需要放在 HTTP 请求的Authorization头中:
Authorization: Bearer __YOUR_API_TOKEN__通过 Prisma CLI 获取 API token
获取 API token 最简单的方式是使用 Prisma CLI 的prisma token命令:
prisma token当在包含prisma.yml的目录中运行时,CLI 会读取prisma.yml中的secret属性并生成对应的 JWT。
从源码看,该命令实现在 cli/packages/prisma-cli-core/src/commands/token/token.ts:它会读取服务的名称与 stage,调用definition.getToken(serviceName, stage)生成 token,并支持--copy(复制到剪贴板)、--env-file、--project等参数。若prisma.yml中未设置 secret,命令会提示There is no secret set in the prisma.yml。
在 GraphQL Playground 中认证
获取 API token 后,即可用它来认证 API 请求(例如通过 GraphQL Playground 使用 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:
- 过期时间:
exp,token 的过期时间 - 服务信息:
service,服务的名称和 stage
示例 JWT Payload:
{ "exp": 1300819380, "service": "my-service@prod" }未来可能会引入更细粒度的访问控制,例如
["write:Log", "read:*"]这样的角色概念。
在 JavaScript 中生成服务 token
考虑以下prisma.yml(使用了环境变量):
service: my-service stage: ${env:PRISMA_STAGE} cluster: ${env:PRISMA_CLUSTER} datamodel: database/datamodel.graphql secret: ${env:PRISMA_SECRET}Node 服务端可以基于jsonwebtoken库,为服务my-service的 stagePRISMA_STAGE生成签名 JWT:
var jwt = require('jsonwebtoken') jwt.sign( { data: { service: 'my-service@' + process.env.PRISMA_STAGE, }, }, process.env.PRISMA_SECRET, { expiresIn: '1h', } )JWT 验证规则
对 Prisma 服务的请求会验证 JWT 的以下属性:
- 必须使用为该服务配置的 secret 签名
- 必须包含
expclaim,且过期时间在未来 - 必须包含
serviceclaim,且服务名与 stage 与当前请求匹配
从服务端源码 server/libs/auth/src/main/scala/com/prisma/auth/Auth.scala 可以看到验证的底层实现:服务端使用Jwt.decodeRaw解码Authorization头(剥离Bearer前缀),并依次校验签名与过期时间;若服务未配置任何 secret(secrets.isEmpty),则直接放行认证。这也解释了为什么prisma.yml中的secret是 API 安全的第一道防线。
错误处理
当查询或变更出错时,响应中会包含errors属性,携带错误code、message等详细信息。
Prisma API 有两类错误:
- Application errors(应用错误):通常表示你的请求无效
- Internal server errors(内部服务器错误):通常表示 Prisma 服务内部发生了意外情况,需要查看服务日志定位问题
注意:
errors字段遵循官方 GraphQL 错误处理规范。
应用错误排查
API 返回错误通常意味着请求的查询或变更存在不正确之处——可能是笔误、遗漏了必填参数等。请对照错误信息检查输入。
常见错误示例——认证失败 / 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 是否已过期、是否由prisma.yml中列出的 secret 签名。
内部服务器错误排查
可查阅服务日志获取更多错误信息。对于本地集群,可以使用prisma logs命令。
延伸阅读
Prisma API 三大操作类型的完整指南均位于同目录下:
- Concepts(核心概念):节点选择、批量操作、连接查询、事务性变更、级联删除
- Queries(查询):对象查询与连接查询、跨关系查询、
orderBy/where/分页等查询参数 - Mutations(变更):对象变更、嵌套变更、标量列表变更、批量变更
- Subscriptions(订阅):类型订阅、订阅请求(WebSocket 协议)、组合订阅与高级过滤
如果你希望深入服务端如何从数据模型生成这套 GraphQL schema,可以阅读 server/servers/api/src/main/scala/com/prisma/api/schema 目录下的源码;如果你对 CLI 如何打通 Playground 与 token 流程感兴趣,可以查看 cli/packages/prisma-cli-core/src/commands/playground 与 cli/packages/prisma-cli-core/src/commands/token 的源码实现。
【免费下载链接】prisma1💾 Database Tools incl. ORM, Migrations and Admin UI (Postgres, MySQL & MongoDB) [deprecated]项目地址: https://gitcode.com/gh_mirrors/pr/prisma1
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考