前言
前面介绍了query、mutation这两种类型的操作,今天将要介绍最后一种类型的操作:subscription
这里对这三种类型的操作做一个简单的总结:
- query 查询:获取数据,比如查找,CRUD 中的 R
- mutation 变更:对数据进行变更,比如增加、删除、修改,CRUD 中的 CUD
- substription 订阅:当数据发生更改,进行消息推送
此外,这三种类型都需要由解析函数 Resolver 来提供数据,比如
query { hi }
那么同名的解析函数resolver(一般也是写在后端)应该是这样的
Query: { hi (parent, args, context, info) { return ... } }
解析函数接受四个参数,分别为
- parent:当前上一个解析函数的返回值
- args:查询中传入的参数
- context:提供给所有解析器的上下文信息
- info:一个保存与当前查询相关的字段特定信息以及 schema 详细信息的值
解析函数的返回值可以是一个具体的值,也可以是 Promise 或 Promise 数组。
此外还需要知道Schema这个文件:
它定义了字段的类型、数据的结构,描述了接口数据请求的规则,当我们进行一些错误的查询的时候 GraphQL 引擎会负责告诉我们哪里有问题,和详细的错误信息,对开发调试十分友好。
Schema一般使用一个简单的强类型模式语法,称为模式描述语言(Schema Definition Language, SDL)
# src/schema.graphql # Query 入口 type Query { hello: String users: [User]! user(id: String): [User]! } # Mutation 入口 type Mutation { createUser(id: ID!, name: String!, email: String!, age: Int,gender: Gender): User! updateUser(id: ID!, name: String, email: String, age: Int, gender: Gender): User! deleteUser(id: ID!): User } # Subscription 入口 type Subscription { subsUser(id: ID!): User } type User implements UserInterface { id: ID! name: String! age: Int gender: Gender email: String! } # 枚举类型 enum Gender { MAN WOMAN } # 接口类型 interface UserInterface { id: ID! name: String! age: Int gender: Gender }
这里的 Schema 文件(这个文件一般写在后端)从 Query、Mutation、Subscription 入口开始定义了各个对象类型或标量类型,这些字段的类型也可能是其他的对象类型或标量类型,组成一个树形的结构,而用户在向服务端发送请求的时候,沿着这个树选择一个或多个分支就可以获取多组信息。
正文
这里主要通过一个实例来理解,先看个图来理解大体上的一个逻辑:
通过订阅这样的一个操作在服务端注册了了一个订阅查询,如果服务端有对应的更新,就会触发这个所订阅的查询,最后会在客户端实时更新。
下面通过一个例子来理解一下Subscription:
当前端发起订阅请求之后,如果后端发现数据改变,可以给前端推送实时信息。
Schema文件:
# Subscription 入口 type Subscription { subsUser(id: ID!): User } type User { id: ID! name: String! age: Int email: String! }
对应resolver:
import Db from'../db' const { PubSub, withFilter } = require('apollo-server') const pubsub = new PubSub() const USER_UPDATE_CHANNEL = 'USER_UPDATE' exportdefault { Mutation: { updateUser: (parent, { id, name, email, age }) => Db.user({ id }) .then(existUser => { if (!existUser) thrownewError('没有这个id的人') return existUser }) .then(() => Db.updateUser({ id, name, email, age })) .then(user => { pubsub.publish(USER_UPDATE_CHANNEL, { subsUser: user }) return user }) }, Subscription: { subsUser: { subscribe: withFilter( (parent, { id }) => pubsub.asyncIterator(USER_UPDATE_CHANNEL), (payload, variables) => payload.subsUser.id === variables.id ), resolve: (payload, variables) => { console.log('🚢 接收到数据:', payload) } } } }
这里的 pubsub 是 apollo-server 里负责订阅和发布的类,它在接受订阅时提供一个异步迭代器,在后端觉得需要发布订阅的时候向前端发布 payload。withFilter 的作用是过滤掉不需要的订阅消息
我们发布一个订阅请求:
# 请求 subscription subsUser($id: ID!) { subsUser(id: $id) { id name age email } } # 参数 { "id": "2" }
用刚刚的数据更新操作来进行一次数据的更改,然后我们将获取到并打印出 pubsub.publish
发布的 payload,这样就完成了数据订阅。
全文完结。
参考
https://www.apollographql.com/docs/react/data/subscriptions/
https://dgraph.io/blog/post/how-does-graphql-subscription/