Prisma API:核心概念
本文属使用Prisma构建GraphQL服务系列。
数据模型和Prisma database schema
Prisma服务的Prisma API完全以其数据模型为中心。
该API是基于与您的Prisma服务关联的数据模型自动生成的。
Prisma API中暴露的每个操作都与您的数据模型中的模型或关系相关联:
-
Queries
- 查询某个模型的一个或多个节点
- 跨关系查询节点
- 查询跨关系汇总的数据
-
Mutations
- 创建,更新,插入和删除特定模型的节点
- 创建,连接,断开连接,更新和上传跨节点的节点
- 批量更新或删除某个模型的节点
-
Subscriptions
- 得到有关创建,更新和删除节点的通知
定义Prisma API中可用的GraphQL操作的实际GraphQL schema也称为Prisma database schema。
高级API概念
节点选择
Prisma API中的许多操作仅影响数据库中现有节点的一个子集,通常甚至仅影响单个节点。
在这种情况下,您需要一种方法来请求API中的特定节点 - 通常用where
参数。
节点可以通过@unique
指令标记的任何字段进行选择。
type Post {
id: ID! @unique
title: String!
published: Boolean @default(value: "false")
}
以下是需要节点选择的一些场景。
通过email
检索单个节点:
query {
post(where: {
email: "hello@graph.cool"
}) {
id
}
}
更新单个节点的title
:
mutation {
updatePost(
where: {
id: "ohco0iewee6eizidohwigheif"
}
data: {
title: "GraphQL is awesome"
}
) {
id
}
}
一次更新多个节点的published
mutation {
updatePost(
where: {
id_in: ["ohco0iewee6eizidohwigheif", "phah4ooqueengij0kan4sahlo", "chae8keizohmiothuewuvahpa"]
}
data: {
published: true
}
) {
count
}
}
批量操作
节点选择概念的一个应用是批量操作,批量更新或删除已针对大量节点进行了优化。因此,这些突变仅返回有多少节点受到影响,而不是特定节点上的完整信息。
例如,突变updateManyPosts
和deleteManyPosts
提供了一个where
参数来选择特定节点,并返回一个包含受影响节点数的计数字段(请参见上面的示例)。
请注意,批量突变不会触发订阅事件!
连接
与直接返回节点列表的简单对象查询相比,连接查询基于中继连接(Relay Connection)模型。除了分页信息之外,连接还提供了聚合等高级功能。
例如,尽管posts
查询允许您选择特定的Post
节点,但可以通过某些字段对结果进行排序并对结果进行分页,postsConnection
查询还允许您计算所有未发布的帖子。
query {
postsConnection {
# `aggregate` allows to perform common aggregation operations
aggregate {
count
}
edges {
# each `node` refers to a single `Post` element
node {
title
}
}
}
}
事务性突变
Prisma API中非批处理操作的单个突变总是以事务方式执行,即使它们包含可能跨越关系的许多操作。这对于在多种类型上执行多个数据库写入的嵌套变异特别有用。
一个例子是创建一个User
节点和两个将要连接的Post
节点,同时也将User
节点连接到另外两个已存在的Post节点,所有这些都在一个突变中。如果任何提交的操作失败(例如由于违反@unique字段约束),整个突变将回滚!
突变是事务性的,意味着它们是原子( atomic)的和孤立(isolated)的。这意味着在同一嵌套突变的两个独立行为之间,没有其他突变可以改变数据。在完成突变处理之前,单一行为的结果也不能被观察到。
级连删除
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)
}
认证(Authentication)
API 密钥(API secret)
Prisma服务的GraphQL API通常受到您在prisma.yml
中指定为secret
属性的API密钥的保护。
这是一个指定secret
的prisma.yml
的例子:
endpoint: http://localhost:4466/myapi/dev
datamodel: datamodel.graphql
secret: mysecret123 # your API secret
API令牌(API token)
API令牌(token)用于对您的Prisma API进行身份验证。 API secret用于签署一个JWT,它需要在针对您的Prisma API进行的HTTP请求的Authorization标头中使用:
Authorization: Bearer __YOUR_API_TOKEN__
使用Prisma CLI获取API令牌
获取API令牌的最简单方法是使用Prisma CLI中的prisma token
命令:
prisma token
在prisma.yml
可用的目录中运行prisma token
时,CLI将从prisma.yml
读取secret
属性并生成相应的JWT。
在GraphQL Playground中进行身份验证
获得Prisma API的API令牌后,您可以使用它来验证您的API请求。例如,通过GraphQL Playground使用API。
一旦您为您的Prisma API打开了Playground,请打开Playground左下角的HTTP HEADERS字段。然后将您的API token粘贴为Authorization
字段的值:
{
"Authorization": "Bearer __YOUR_API_TOKEN__"
}
实际有了真正的令牌时,像如下这样:
{
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJibG9nckBkZXYiLCJyb2xlcyI6WyJhZG1pbiJdfSwiaWF0IjoxNTE4NzE2NjA4LCJleHAiOjE1MTkzMjE0MDh9.zqBh_Oo4RmV4j3UQeVDYqJDxV-YHQiOR-XIlhjbWejw"
}
![](https://img.haomeiwen.com/i185619/1fae4425cf7a0568.png)
声明
JWT必须包含不同的声明:
-
到期时间:
exp
,令牌的到期时间。 -
服务信息:
service
,服务的名称和stage
将来可能会通过引入诸如["write:Log", "read:*"]
等角色的概念来支持更细粒度的访问控制。
这里是一个JTW的Payload示例。
{
"exp": 1300819380,
"service": "my-service@prod"
}
使用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
一个服务节点创建了JWT标记,基于jsonwebtoken,对于服务my-service
的stage PRISMA_STAGE
是这样的:
var jwt = require('jsonwebtoken')
jwt.sign(
{
data: {
service: 'my-service@' + process.env.PRISMA_STAGE,
},
},
process.env.PRISMA_SECRET,
{
expiresIn: '1h',
}
)
JWT验证
对于针对Prisma服务提出的请求,JWT的以下属性将得到验证:
- 它必须使用为该服务配置的
secret
进行签名 - 它必须包含具有时间值的
exp
声明 - 它必须包含与
service
和stage
匹配的当前请求的服务声明
错误处理
当您的某个查询或突变发生错误时,响应包含错误属性,其中包含有关错误代码,错误消息等的更多信息。
有两种API错误:
- 应用错误(Application errors)通常表明您的请求无效。
- 内部服务错误(Internal server errors)通常意味着Prisma服务内部发生了意外事件。查看您的服务日志以获取更多信息。
注意:
error
字段依据的GraphQL错误处理规范进行。
应用错误
API返回的错误通常表明某些内容对于请求的查询或突变不正确。您可能无意中在您的查询中输入了错字或忘记了必需的参数。尝试调查您的输入以找出与错误消息相关的可能错误。
错误解决
以下是您可能遇到的常见错误列表:
- Authentication
1.1 Insufficient permissions / 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."
}
]
}
检查您提供的令牌是否尚未过期,并使用prisma.yml
中列出的secret
进行签名。
内部服务错误
请查阅服务日志以获取有关该错误的更多信息。对于本地群集,可以使用prisma logs
命令完成此操作。