本文属使用Prisma构建GraphQL服务系列。
概述
GraphQL订阅允许您在数据发生更改时实时得到通知。有三种触发订阅的事件:
- 一个新的节点被创建
- 现有节点已更新
- 现有的节点被删除
这是一个订阅示例,每当创建新的Post
节点时都会通知您。订阅触发时,服务器发送的有效payload将包含Post
的description
和imageUrl
:
subscription newPosts {
post(where: {
mutation_in: [CREATED]
}) {
mutation
node {
description
imageUrl
}
}
}
订阅使用特殊的websocket端点。
以下是可用订阅的列表。要探索它们,请在您的服务中使用GraphQL Playground。
- 对于数据模型中的每种对象类型,都可以使用类型订阅来侦听该类型的数据更改。
- 目前,连接或断开节点间的关系不会触发订阅!
可以在单个订阅请求中合并多个订阅触发器,以准确控制要通知的事件。订阅API还使用可用于查询的丰富过滤器系统。
请求订阅
使用Apollo Client时,您可以使用apollo-link-ws
库来方便订阅。这是一个例子。
您还可以使用GraphQL Playground或任何WebSocket客户端,如下所述。
GraphQL Playground
GraphQL Playground可用来查看和运行GraphQL 订阅
普通WebSockets
1.建立连接
订阅通过WebSockets进行管理。首先建立一个WebSocket连接并指定graphql-subscriptions
协议:
let webSocket = new WebSocket('wss://__CLUSTER__.prisma.sh/__WORKSPACE__/__SERVICE__/__STAGE__', 'graphql-subscriptions');
2.发起握手
接下来,您需要启动WebSocket服务器的握手。您可以通过监听open
事件然后发送JSON消息到type
属性设置为init
的服务来执行此操作:
webSocket.onopen = (event) => {
const message = {
type: 'init'
}
webSocket.send(JSON.stringify(message))
}
3.接收消息
服务可以用各种类型属性来区分各种消息。您可以根据您的应用程序对每条消息作出反应:
webSocket.onmessage = (event) => {
const data = JSON.parse(event.data)
switch (data.type) {
case 'init_success': {
console.log('init_success, the handshake is complete')
break
}
case 'init_fail': {
throw {
message: 'init_fail returned from WebSocket server',
data
}
}
case 'subscription_data': {
console.log('subscription data has been received', data)
break
}
case 'subscription_success': {
console.log('subscription_success')
break
}
case 'subscription_fail': {
throw {
message: 'subscription_fail returned from WebSocket server',
data
}
}
}
}
4.订阅数据更改
要订阅数据更改,请发送type
属性设置为subscription_start
的消息:
const message = {
id: '1',
type: 'subscription_start',
query: `
subscription newPosts {
post(filter: {
mutation_in: [CREATED]
}) {
mutation
node {
description
imageUrl
}
}
}
`
}
webSocket.send(JSON.stringify(message))
您应该收到type
设置为subscription_success
的消息。发生数据更改时,您将收到type
设置为subscription_data
的消息。您在subscription_start
消息中提供的id
属性将显示在所有subscription_data
消息中,允许您复用WebSocket连接。
5.取消订阅
要取消订阅数据更改,请发送type
属性设置为subscription_end
的消息:
const message = {
id: '1',
type: 'subscription_end'
}
webSocket.send(JSON.stringify(message))
类型订阅(Type subscriptions)
对于数据模型中的每种可用对象类型,都会自动生成某些订阅。 作为一个例子,考虑使用单个Post
类型的以下数据模型:
type Post {
id: ID! @unique
title: String!
description: String
}
在生成的Prisma API中,可以使用post
订阅,只要某些Post
类型的节点被创建,更新或删除,就可以通知post
订阅。
创建节点的订阅(Subscribing to created nodes)
对于给定类型,您可以订阅正在使用生成的类型订阅创建的所有节点。
订阅所有创建的节点
如果要订阅Post
类型的节点创建,可以使用post
订阅并指定where
对象并设置mutation_in : [CREATED]
。
subscription {
post(where: {
mutation_in: [CREATED]
}) {
mutation
node {
description
imageUrl
author {
id
}
}
}
}
有效载(payload)荷包含:
-
mutation
: 此时,它将返回CREATED
-
node
: 允许您在创建的节点(以及可能的相关节点)上查询信息。
订阅特定的创建节点
对于查询使用的where
对象参数,可以使用类似的查询(Queries)中的过滤机制。
例如,用户关注个别author
,只通知关注者,推主创建了帖子:
subscription {
post(where: {
AND: [{
mutation_in: [CREATED]
}, {
node: {
author: {
followedBy_some: {
id: "cj03x3nacox6m0119755kmcm3"
}
}
}]
}) {
mutation
node {
description
imageUrl
author {
id
}
}
}
}
订阅已删除的节点
订阅所有已删除的节点
如果要订阅Post类型的更新节点,可以使用post
订阅并指定where
对象并设置mutation_in: [DELETED]
。
subscription deletePost {
post(where: {
mutation_in: [DELETED]
}) {
mutation
previousValues {
id
}
}
}
有效载荷(payload)包含:
-
mutation
: 返回DELETED
-
previousValues
: 以前的节点标量值。
注意:
previousValues
在CREATED
订阅中始终返回null
订阅特定的已删除节点
与CREATED
订阅类似,对于查询使用的where
对象参数,可以使用类似的查询(Queries)中的过滤机制。
例如,如果特定用户关注作者,只能通知已删除的帖子:
subscription {
post(where: {
mutation_in: [DELETED]
node: {
author: {
followedBy_some: {
id: "cj03x3nacox6m0119755kmcm3"
}
}
}
}) {
mutation
previousValues {
id
}
}
}
订阅更新的节点(Subscribing to updated nodes)
对于给定的类型,您可以使用生成的类型订阅订阅正在更新的所有节点。
订阅所有更新的节点
如果您想订阅Post
类型的更新节点,则可以使用post
订阅并指定where
对象并设置mutation_in : [UPDATED]
。
subscription {
post(where: {
mutation_in: [UPDATED]
}) {
mutation
node {
description
imageUrl
author {
id
}
}
updatedFields
previousValues {
description
imageUrl
}
}
}
有效载荷(payload)包含:
-
mutation
:在这种情况下,它将返回UPDATED。 -
node
:允许您查询更新节点和连接节点上的信息。 -
updatedFields
:已更改的字段列表。 -
previousValues
:节点的以前标量值。
注意:
updatedField
s对于CREATED
和DELETED
订阅始终为null
。对于CREATED
订阅,previousValues
始终为null
。
订阅更新的字段
例如,如果Post
中的description
字段发生更改,则只能通知订阅:
subscription {
post(where: {
mutation_in: [UPDATED]
updatedFields_contains: "description"
}) {
mutation
node {
description
}
updatedFields
previousValues {
description
}
}
}
与updatedFields_contains
类似,存在更多的过滤条件:
-
updatedFields_contains_every: [String!]
: 如果指定的所有字段都已更新,则匹配。 -
updatedFields_contains_some: [String!]
: 如果某些指定的字段已更新,则匹配。
注意:您不能将
updatedFields
过滤器条件与mutation_in: [CREATED]
或者mutation_in: [DELETED]
一起使用!
关系订阅(Relation subscriptions)
目前,关系更新订阅仅在使用UPDATED
订阅的解决方法中可用。
订阅关系变更(Subscribing to relation changes)
您可以通过touching节点来强制进行通知更改。将dummy: String
字段添加到正在讨论的类型中,并更新此字段以查找关系状态刚更改的节点。
mutation updatePost {
updatePost(
where: {
id: "some-id"
}
data: {
dummy: "dummy" # do a dummy change to trigger update subscription
}
)
}
合并订阅(Combining subscriptions)
您可以在一个订阅中订阅同一类型的多个突变。
订阅所有节点的更改(Subscribe to all changes to all nodes)
使用where
对象的mutation_in
参数,可以选择要订阅的突变类型。例如,要订阅createPost
,updatePost
和deletePost
突变:
subscription {
post(where: {
mutation_in: [CREATED, UPDATED, DELETED]
}) {
mutation
node {
id
description
}
updatedFields
previousValues {
description
imageUrl
}
}
}
订阅对特定节点的所有更改(Subscribe to all changes to specific nodes)
要选择要通知的特定节点,请使用where
对象的节点参数。你可以把它和mutation_in
结合起来。例如,如果特定用户关注作者,只能通知创建,更新和删除的帖子:
subscription {
post(
where: {
mutation_in: [CREATED, UPDATED, DELETED]
}
node: {
author: {
followedBy_some: {
id: "cj03x3nacox6m0119755kmcm3"
}
}
}
) {
mutation
node {
id
description
}
updatedFields
previousValues {
description
imageUrl
}
}
}
高级订阅过滤器(Advanced subscription filters)
示例,您可以订阅所有CREATED
和DELETE
订阅,以及更新imageUrl
时的所有UPDATED
订阅:
subscription {
post(where: {
OR: [{
mutation_in: [CREATED, DELETED]
}, {
mutation_in: [UPDATED]
updatedFields_contains: "imageUrl"
}]
}) {
mutation
node {
id
description
}
updatedFields
previousValues {
description
imageUrl
}
}
}
注意:将任何
updatedFields
过滤条件与CREATED
或DELETED
订阅一起使用会导致错误。对于CREATED
预订,previousValues
始终为null
,对于CREATED
和DELETED
预订,updatedFields
始终为null
。