使用Vue.js和AWS AppSync(GraphQL)创建了聊天应用程序
首先
我之前写过一篇名为”使用Vue.js×GraphQL(AppSync)×Amplify创建TODO列表”的文章。基于这篇文章,给团队成员做了一个实操,大家对于使用GraphQL感觉相当容易和受欢迎。上次我们使用了Mutation和Query构建了一个TODO列表风格的备忘录应用。这次我们尝试使用GraphQL的Subscription来创建一个聊天应用。
总结。
突如其来地总结一下。
或者说,我先在这篇文章中传达想要表达的内容。
-
- GraphQLはRESTAPIに比べてハードルは高いかもしれない…
Subscription はGraphQLの中で一番便利!(120%主観)
Messangerなどのチャットアプリの裏側をなんとなく知ることができる
源代码
源代码已经推送到了 GitHub 上。
我们会同时参考部分源代码进行解释,但是如果您想要详细了解,请从这里确认。
需要注意的是,Vue 文件的结构是使用 pug + Typescript + scss 进行的。
vue-graphql-chat-source
建筑设计 zhù shè jì)
做出的东西
登录相关的实施
我正在使用 AWS Amplify 的 Vue.js 版本 aws-amplify-vue 进行实现。
aws-amplify-vue 提供了登录表单等组件。
有关更详细的信息,请查看如何使用 aws-amplify 在 Vue.js 中快速实现登录页面。我已经在这篇文章中总结了相关内容。
实现聊天部分
本题从这里开始。
我们将使用GraphQL来实现聊天应用程序的核心部分。
聊天的机制 de
首先,作为聊天应用程序的主要功能,可以列举如下。
-
- 发送消息
- 接收消息
虽然很杂,但如果有这两个功能,可以说是一个聊天应用吗?
消息的发送是由客户端触发的,因此可以很容易地使用REST进行实现。
但是如果尝试使用REST实现消息接收呢?
最先想到的是轮询。每隔几秒或几分钟获取数据,如果有更改,就更新屏幕。这样的流程。
但是在这种情况下,不能真正实时更新…特别是在聊天的情况下,希望在消息发送后立即更新屏幕。
解决这个问题的是GraphQL的Subscription功能。
订阅
当GraphQL执行特定的Mutation时,会作为Subscription将消息发送到客户端。
{
"onCreateChatMessage": {
"create_time": "2019-02-05 15:51:43",
"message_body": "テスト投稿",
"user_id": "sato"
}
}
Vue.js 中使用 AppSync(GraphQL)的基本方法。
Amplify的初始设置
将模块导入到 main.ts,并加载到 Vue 中,然后进行 Amplify 的初始设置。
* 这里的 aws-exports.js 是包含 Cognito 和 AppSync 信息的文件。请自行准备该文件,或使用 AmplifyCLI 生成。
import Amplify, * as AmplifyModules from "aws-amplify";
import { AmplifyPlugin } from "aws-amplify-vue";
import aws_exports from "./aws-exports";
Amplify.configure(aws_exports);
Vue.use(AmplifyPlugin, AmplifyModules);
const awsmobile = {
"aws_project_region": "",
"aws_cognito_identity_pool_id": "",
"aws_cognito_region": "",
"aws_user_pools_id": "",
"aws_user_pools_web_client_id": "",
"aws_appsync_graphqlEndpoint": "",
"aws_appsync_region": "",
"aws_appsync_authenticationType": ""
};
export default awsmobile;
使用AppSync
使用Amplify提供的API模块。
import { API, graphqlOperation } from "aws-amplify";
async appSyncFunction() {
await API.graphql(graphqlOperation(gqlParams));
}
AppSync的模式定义(部分)
下面是AppSync模式定义的一部分。大部分内容是由AppSync默认生成的,但也添加了一些自定义内容。
type ChatMessage {
user_id: ID!
create_time: String!
message_body: String!
}
type Mutation {
createChatMessage(input: CreateChatMessageInput!): ChatMessage
updateChatMessage(input: UpdateChatMessageInput!): ChatMessage
deleteChatMessage(input: DeleteChatMessageInput!): ChatMessage
}
type Query {
getChatMessage(user_id: ID!, create_time: String!): ChatMessage
listChatMessages(filter: TableChatMessageFilterInput, limit: Int, nextToken: String): ChatMessageConnection
}
type Subscription {
onCreateChatMessage: ChatMessage
@aws_subscribe(mutations: ["createChatMessage"])
onCreateChatMessageByUserId(user_id: ID, create_time: String, message_body: String): ChatMessage
@aws_subscribe(mutations: ["createChatMessage"])
onUpdateChatMessage(user_id: ID, create_time: String, message_body: String): ChatMessage
@aws_subscribe(mutations: ["updateChatMessage"])
onDeleteChatMessage: ChatMessage
@aws_subscribe(mutations: ["deleteChatMessage"])
onDeleteChatMessageByUserId(user_id: ID, create_time: String, message_body: String): ChatMessage
@aws_subscribe(mutations: ["deleteChatMessage"])
}
消息发送处理(变更)
发送消息使用Mutation。
/**
* チャットメッセージ送信
*/
public async createMessage() {
const gqlParams: string = `
mutation put {
createChatMessage(
input: {
user_id: "${VueStore.state.userID}",
create_time: "${dayjs().format("YYYY-MM-DD HH:mm:ss")}",
message_body: "${this.putMessage}"
}
) {
user_id,
create_time,
message_body
}
}
`;
await API.graphql(graphqlOperation(gqlParams));
}
获取消息处理(查询)
使用查询来获取消息。
/**
* 最新50件分チャットメッセージを取得
*/
private async getMessages() {
const gqlParams: string = `
query list {
listChatMessages(limit: 50) {
items {
user_id,
create_time,
message_body
}
}
}
`;
const result: any = await API.graphql(graphqlOperation(gqlParams));
// 取得したメッセージ
const messages: ChatMessagesType[] = result.data.listChatMessages.items;
// scanで取得してくるので時系列をソートする必要がある
this.chatMessages = messages.sort((a, b) => {
return dayjs(a.create_time).unix() - dayjs(b.create_time).unix();
});
}
订阅处理(Subscription)
我们将使用Subscription进行。
在这个例子中,我们通过触发所有用户发送的信息来执行客户端界面的更新处理。
/**
* メッセージ新規作成のsubscribe
*/
private async createGqlSubscriber() {
const gqlParams: string = `
subscription subCreateChatMessage {
onCreateChatMessage {
user_id,
create_time,
message_body
}
}
`;
this.subCreateChatMessageObservable = await API.graphql(graphqlOperation(gqlParams, {
deleted: false
})) as Observable<object>;
// subscribeセッションを確立する
this.subCreateChatMessageClient = this.subCreateChatMessageObservable.subscribe({
// GraphQLからメッセージを受け取ったときの処理
next: (result: any) => {
console.log(result.value.data);
this.getMessages();
},
error: (err: any) => {
console.error(err);
}
});
}
可以仅针对特定用户发送消息。
下面的示例是希望当 user_id 为 sato 时触发消息发送的情况。
const gqlParams: string = `
subscription subCreateChatMessage {
onCreateChatMessageByUserId (
user_id: "sato"
) {
user_id,
create_time,
message_body
}
}
`;
最后
GraphQL的订阅功能比自己编写轮询更容易实现,而且它并不是持续发送请求,因此对资源的负载较小。
我们一直在开发可以实时更新图形的应用程序,但由于只是定期调用获取数据的API,所以如果时间间隔太短,会增加数据库的负载问题。如果使用GraphQL的订阅功能,这些问题是否会得到解决呢?我们正在进行验证。
但是,正如前面提到的,相比REST,GraphQL的门槛较高。我期待GraphQL更加流行,并降低这一门槛的那一天。
再见!