使用ApolloServer探索GraphQL的Subscription功能
首先
这篇文章是上一篇关于介绍GraphQL概述,并确认Query和Mutation的操作的续篇。本文将讨论GraphQL的订阅功能。
“Subscription(订阅)是一个熟悉的词汇,直译为“购买”,在GraphQL中指的是在Mutation操作数据时,客户端收到来自服务器发出的变更信息,可用于聊天应用程序、通知功能、登录认证等。推荐这里的解释,非常易懂。”
这一次,我们将使用Apollo来替代之前建立的Express-GraphQL服务器,搭建GraphQL服务器,并检查Subscription的运作情况。
“Apollo是什么意思?”
Apollo是一个用于构建数据图的平台,它是一个连接前端应用程序客户端(如React或iOS应用程序)与后端服务无缝通信的通信层。
通过位于客户端和服务器之间的Apollo服务器,GraphQL数据交互变得更加流畅。

公式文件
准备好
创建项目后,通过npm安装apollo-server。
mkdir graphql-server
cd graphql-server
mkdir resolver
touch index.js db.js resolver/{Query.js,Mutation.js,Subscription.js}
npm init
npm install apollo-server apollo-server-express --save
为了使示例代码更易读
发布-订阅模式
PubSub就像一个工厂,通过Subscription创建通知事件。
const { PubSub } = require('apollo-server');
const pubsub = new PubSub();
高翘了。
使用称为gql的标记模板文字面量函数,可以将与GraphQL相关的定义表示为JavaScript字符串。
const typeDefs = gql`
type Subscription {
postAdded: Post
}
type Query {
posts: [Post]
}
type Mutation {
addPost(author: String, comment: String): Post
}
type Post {
author: String
comment: String
}
`
Resolver参数
在解析函数中,有四个固定参数扮演着特定的角色。
代码介绍和解释
请写下代码到已创建的文件中。
由于篇幅原因,我们会省略对于Query和Mutation的概述,初次接触GraphQL的朋友们可以从这篇文章开始阅读!?
首先,准备一个用作数据库替代的JavaScript文件。
const posts = [{
id: '1',
title: 'こころ',
author: '夏目漱石'
}, {
id: '2',
title: '舞姫',
author: '森鴎外'
}, {
id: '3',
title: '羅生門',
author: '芥川龍之介'
}]
const db = {
posts,
}
module.exports = db;
接下来,我们将依次准备包含Query、Mutation和本次主题的Subscription的解析器函数所在的文件。
const Query = {
posts(parent, args, { db }, info) {
//クエリを書いた時に引数が「ない」時
//模擬データベースの内容を全て表示
if (!args.query) {
return db.posts
//クエリを書いた時に引数が「ある」時
//引数とtitle or authorが一致したものだけを表示
}else{
return db.posts.filter((post) => {
const isTitleMatch = post.title.toLowerCase().includes(args.query.toLowerCase())
const isAuthorMatch = post.author.toLowerCase().includes(args.query.toLowerCase())
return isTitleMatch || isAuthorMatch
})
}
}
}
module.exports = Query
这是一个查询解析函数。根据参数的有无进行条件分支。通过JavaScript的详细条件编写,GraphQL的优势之一是能够只获取所需的必要信息。
const Mutation = {
createPost(parent, args, { db, pubsub }, info) {
const postNumTotal = String(db.posts.length + 1)
const post = {
id: postNumTotal,
...args.data
}
//データベース更新
db.posts.push(post)
//Subscription着火
pubsub.publish('post', {
post: {
mutation: 'CREATED',
data: post
}
})
return post
},
updatePost(parent, args, { db, pubsub }, info) {
const { id, data } = args
const post = db.posts.find((post) => post.id === id)
if (!post) {
throw new Error('Post not found')
}
if (typeof data.title === 'string' && typeof data.author === 'string') {
//データベース更新
post.title = data.title
post.author = data.author
console.log(post)
//Subscription着火
pubsub.publish('post', {
post: {
mutation: 'UPDATED',
data: post
}
})
}
return post
},
deletePost(parent, args, { db, pubsub }, info) {
const post = db.posts.find((post) => post.id === args.id)
const postIndex = db.posts.findIndex((post) => post.id === args.id)
if (postIndex === -1) {
throw new Error('Post not found')
}
//データベース更新
db.posts.splice(postIndex, 1)
//Subscription着火
pubsub.publish('post', {
post: {
mutation: 'DELETED',
data: post
}
})
return post
},
}
module.exports = Mutation
在Mutation的解析器函数中,同时进行了数据库的更新和启动Subscription。
const Subscription = {
post: {
subscribe(parent, args, { pubsub }, info) {
return pubsub.asyncIterator('post')
}
}
}
module.exports = Subscription
这是一个订阅的解析器函数。
它使用pubsub.asyncIterator来异步监听订阅的事件。
因为说明的关系,这是最后的部分,包括了模式定义和服务器启动文件。
const {ApolloServer,PubSub,gql} = require('apollo-server');
const db = require('./db')
const Query = require('./resolver/Query')
const Mutation = require('./resolver/Mutation')
const Subscription = require('./resolver/Subscription')
//スキーマ定義
const typeDefs = gql`
type Query {
posts(query: String): [Post!]!
}
type Mutation {
createPost(data: CreatePostInput!): Post!
deletePost(id: ID!): Post!
updatePost(id: ID!, data: UpdatePostInput!): Post!
}
# Subscription
type Subscription {
post: PostSubscriptionPayload!
}
input CreatePostInput {
title: String!
author: String!
}
input UpdatePostInput {
title: String
author: String!
}
type Post {
id: ID!
title: String!
author: String!
}
######################
# Subscriptionで利用
######################
enum MutationType {
CREATED
UPDATED
DELETED
}
# Subscriptionのフィールド
type PostSubscriptionPayload {
mutation: MutationType!
data: Post!
}
`
//PubSubのインスタンスを作成,Subscriptionが利用可能に!
const pubsub = new PubSub()
const server = new ApolloServer({
typeDefs: typeDefs,
resolvers: {
Query,
Mutation,
Subscription,
},
context: {
db,
pubsub
}
})
server.listen().then(({ url, subscriptionsUrl }) => {
console.log(`? Server ready at ${url}`);
console.log(`? Subscriptions ready at ${subscriptionsUrl}`);
});
在搭建服务器时,指定了模式(schema),解析器(resolver),消息发布与订阅(PubSub)等作为参数。
指定的参数在查询(Query),变更(Mutation)和订阅(Subscription)的各个处理中被使用。
当准备好之后,通过终端启动。
node index.js
? Server ready at http://localhost:4000/
? Subscriptions ready at ws://localhost:4000/graphql
我会像上次一样在GraphQL IDE中进行测试。
要使用Subscription,您需要在编写查询之后,点击一个类似YouTube播放按钮的箭头。
当它变成像照片中的红色暂停按钮一样的时候,表示Subscription成功启动。



我会确认数据的更新和删除。



正在进行订阅,确认数据删除的操作(正在连续进行更新和删除)。

我们通过订阅成功确认了通过突变进行数据更新!!??
用中文进行自然改写:
最后
尽管相对于Query和Mutation来说,Subscription理解起来需要花费更多的时间,但通过编写实际的代码并在GraphQL IDE中进行测试,我成功地将其融入自己的理解中。
在下一篇文章中,我将使用Vue作为前端,并在实际应用程序中使用GraphQL!
好的,再见!?