使用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ì)

vue-graphql-chat.png

做出的东西

vue-graphql-chat.gif

登录相关的实施

我正在使用 AWS Amplify 的 Vue.js 版本 aws-amplify-vue 进行实现。
aws-amplify-vue 提供了登录表单等组件。
有关更详细的信息,请查看如何使用 aws-amplify 在 Vue.js 中快速实现登录页面。我已经在这篇文章中总结了相关内容。

实现聊天部分

本题从这里开始。
我们将使用GraphQL来实现聊天应用程序的核心部分。

聊天的机制 de

首先,作为聊天应用程序的主要功能,可以列举如下。

    1. 发送消息

 

    接收消息

虽然很杂,但如果有这两个功能,可以说是一个聊天应用吗?
消息的发送是由客户端触发的,因此可以很容易地使用REST进行实现。
但是如果尝试使用REST实现消息接收呢?
最先想到的是轮询。每隔几秒或几分钟获取数据,如果有更改,就更新屏幕。这样的流程。
但是在这种情况下,不能真正实时更新…特别是在聊天的情况下,希望在消息发送后立即更新屏幕。
解决这个问题的是GraphQL的Subscription功能。

订阅

graphql_pubsub.png

当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更加流行,并降低这一门槛的那一天。
再见!

广告
将在 10 秒后关闭
bannerAds