使用 AWS Amplify 和 Nuxt.js 创建一个可以让用户登录的 Web 聊天应用

AWS的Amplify只需设计GraphQL,便能自动生成后端代码、资源和配置,只需使用js或其他语言编写前端代码便可创建Web应用程序。我阅读了文档,并为了学习的目的尝试创建了一个能够进行用户管理的Web聊天应用,以下为笔记。

前提 tí) – premise

    • AWSのアカウントは取得済み

 

    • Amplify CLIはインストール+設定済み

 

    Nuxt.jsをインストールする準備は完了済み

环境

    • フロント: NuxtJS + Vuetify

 

    • バックエンド: AWS Amplify

 

    API設計: GraphQL

环境参考资料:
安装 Amplify CLI
安装 Nuxt.js

制作之物

这是一个简单的网络聊天工具。用户可以注册并登录,然后在聊天中发布评论。

undefined

积分 (jī

    • ユーザー登録、ログインができる。

 

    • ユーザー毎にコメントできる

 

    • コメントはリアルタイムで画面に反映

 

    • ログインしないとコメント投稿・閲覧ができない

 

    投稿したコメントはDBに保存されて再度ログインしたときには続きが書き込める

我們的目標是朝著這個方向努力。

安装Nuxt.js

首先在前端进行Nuxt的安装并创建基本页面。我将项目命名为nuxt-amplify。
我选择了以下选项。

% npx create-nuxt-app nuxt-amplify

create-nuxt-app v3.0.0
✨  Generating Nuxt.js project in nuxt-amplify
? Project name nuxt-amplify
? Choose programming language JavaScript
? Choose the package manager Npm
? Choose UI framework Vuetify.js
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support, Content
? Choose linting tools ESLint, Prettier, Lint staged files, StyleLint
? Choose test framework Jest
? Choose rendering mode Single Page App
? Choose development tools jsconfig.json (Recommended for VS Code)

创建基本页面

在前台页面上,我们会适当地调整外观。
只需要一个包含评论输入框和内容列表的部分即可。

<template>
  <div style="max-width: 800px;">
    <v-text-field
      label="コメント"
      placeholder="ここにコメントを書きましょう"
      outlined
      class="mx-auto"
      append-icon="mdi-check-bold"
      style="max-width: 100%; box-sizing: border-box;"
    ></v-text-field>

    <v-card v-for="(item, index) in items" :key="index" tile>
      <v-list-item two-line>
        <v-list-item-content>
          <v-list-item-title>{{ item.comment }}</v-list-item-title>
          <v-list-item-subtitle>by: {{ item.owner }}</v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </v-card>
  </div>
</template>
<script>
export default {
  data() {
    return {
      form: {
        comment: '',
      },
      items: [],
    }
  },
  created() {
    this.getChatList()
  },
  methods: {
    getChatList() {
      // コメント取得
      this.items = [
        {
          comment: 'ここにコメントが入ります',
          owner: 'ここに投稿者名が入ります',
        },
        {
          comment: 'ここにコメントが入ります',
          owner: 'ここに投稿者名が入ります',
        },
        {
          comment: 'ここにコメントが入ります',
          owner: 'ここに投稿者名が入ります',
        },
      ]
    },
  },
}
</script>

undefined

安装Amplify

只要准备好前端部分,然后安装Amplify并进行必要的配置,就可以将其作为一个应用程序运行。
首先,在现有的项目中安装Amplify。
在先前创建的Nuxt.js项目目录的根目录中运行amplify init命令。
选择选项如下所示。

% amplify init

? Enter a name for the project nuxtamplify #任意のプロジェクト名
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you re building javascript

? What javascript framework are you using vue
? Source Directory Path:  . # ルートを指定
? Distribution Directory Path: dist # ビルド済ファイルの保存ディレクトリを指定
? Build Command:  npm run build # ビルドコマンドを指定(※)
? Start Command: npm run start # 起動コマンドを指定(※)

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default

我认为关于最后的命令相关问题可能会因环境等因素而有所变化。 可以参考package.json。

加载适用于Vue.js的库。

由于Vue.js提供了与amplify进行通信以及执行各种操作的库,因此我们将添加它。

npm install aws-amplify @aws-amplify/ui-vue

将其配置为Nuxt.js的插件。

首先,您需要创建一个可以用作Nuxt.js插件的文件,并进行相应的配置。

import Vue from 'vue'
import Amplify from 'aws-amplify'
import '@aws-amplify/ui-vue'
import awsExports from '../aws-exports'

Amplify.configure(awsExports)
Vue.use(Amplify)

在nuxt.config.js的插件配置数组中添加{ src: ‘~/plugins/amplify.js’, ssr: false }。

//...

// pluginsの配列に追加します。
plugins: [{ src: '~/plugins/amplify.js', ssr: false }],

//...

创建后端API

终于,在AWS端创建了后端,并生成了端点。
从这里开始,只需使用一些命令和少量代码就能快速创建一个完整的Web应用程序。
首先,向Amplify添加API功能,
只需要执行amplify add api命令。
选项配置如下所答。

% amplify add api

? Please select from one of the below mentioned services: GraphQL
? Provide API name: chat # 任意のAPI名
? Choose the default authorization type for the API API key
? Enter a description for the API key: sample
? After how many days from now the API key should expire (1-365): 7
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields (e.g., “Todo” with ID, name, description) 
? Do you want to edit the schema now? Yes

这样就会将所需的代码添加到项目中。

使用GraphQL进行后端设计

我们将使用GraphQL来设计后端API。
由于基本文件已经创建完成,
请打开amplify/backend/api/目录下的文件并进行如下更改。

type Chat @model {
  id: ID!
  comment: String!,
  owner: String
}

创建一个名为”Chat”的模型(表),并在其中添加id、comment和owner字段以便进行保存和检索。
这是后端设计所需的唯一文件。
GraphQL是一种用于API设计的查询语言,根据这种语言可以自动生成后端的数据库保存和API请求处理等内容。
详细信息请参考以下内容:
– 学习GraphQL的基础知识
– 学习API(GraphQL)的指令(在Amplify中使用的指令)
(虽然我也刚刚开始学习,但对于之前编写过Web后端代码的人来说,这是非常令人兴奋的内容)

向AWS发送并生成所需的资源。

只需执行amplify push命令,一旦完成,AWS将自动创建所需的资源并实现协作。

% amplify push

? Are you sure you want to continue? Yes

? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js # エンドポイント側と通信する各種コードが生成されるフォルダを指定、とりあえずデフォルトで。
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes
? Enter maximum statement depth [increase from default if your schema is deeply nested] 2

稍等一下,它需要一些时间才能完成(有时会偶尔问一些问题,但基本上是默认设置),会自动帮你进行AWS端的配置。
以上,API终端点的生成已完成。
几乎不用写什么代码就可以实现。

(※)

输入GraphQL查询的文件名模式…
对于与GraphQL相关的文件,我应该把它们放在哪里呢?在Nuxt.js中最好放在哪个位置?似乎有更智能的指定方法…

将前端部分进行修改,使其能够与API端点进行通信。

因为已经启动了终端节点,所以需要修改前端代码以进行通信。

<template>
・・・
// v-modelとイベントハンドラを追加
<v-text-field
        ...
        v-model="form.comment"
        @keydown="onEnter"
        @click:append="createChat"
      ></v-text-field>

...
<template>
<script>
import { API } from 'aws-amplify' // Amplifyライブラリを読み込み
import { createChat } from '~/src/graphql/mutations' // GraphQL Mutation(データをエンドポイントに送信する構文?)
import { listChats } from '~/src/graphql/queries' // GraphQL Query(データを読み込む構文?)

export default {
  // ...
  methods: {
    async createChat() {
      // コメントを送信する
      const comment = this.form.comment // コメント入力値を取得
      if (!comment) return // 空のときは処理しない
      const chat = { comment } // 送信用のJSONを作成
      // 送信処理
      await API.graphql({
        query: createChat, // GraphQL Mutation
        variables: { input: chat }, // 送信データ
      })
      this.form.comment = '' // 送信後にテキストフィールドを空に。
    },
    onEnter(event) {
      // ここはおまけ。(Enterを押したときもコメントを送信したかったので記述)
      if (event.keyCode !== 13) return
      this.createChat()
    },
    async getChatList() {
      // コメント一覧を取得
      const chatList = await API.graphql({
        query: listChats, // GraphQL Query
      })
      this.items = chatList.data.listChats.items // 読み込みしたデータを一覧に表示
    },
  },
}
</script>

在执行amplify push时,mutations和queries会在指定的目录中生成。
通过查看它们的内容,我们可以更好地理解并能够进行各种应用。

为了实时反映,添加代码。

另外,我们还需要添加代码,使得保存的内容能够实时反映在屏幕上,不仅仅是发送和加载。我们将使用GraphQL的Subscription功能来接收来自服务器的推送通信等。

<script>
// ...
import { onCreateChat } from '~/src/graphql/subscriptions' // GraphQL Subscription

export default {
  // ...
  created() {
    this.getChatList() 
    this.subscribe() // 追加
  },
  methods: {
    //...

    // メソッド追加
    subscribe() {
      API.graphql({ query: onCreateChat }).subscribe({ 
        next: (eventData) => {
          // コメントが送信されて追加されたとき、送信内容を一覧に追加
          const chat = eventData.value.data.onCreateChat // データを読み込み
          if (this.items.some((item) => item.comment === chat.comment)) return // すでに表示されているデータは無視
          this.items = [...this.items, chat] // 新しいデータを追加
        },
      })
    },
  },
}
</script>

以上完成了对API的响应。
作为聊天应用程序,它已经可以运行了。

undefined

請在評論區輸入文字,然後點擊「Enter」或勾選,您將看到數據在列表中反映出來。
(發布者名稱尚未填寫,我們將要進行實作)
另外,您可以嘗試登入AWS控制台,您將看到已經建立了DynamoDB表格,並且數據已經被保存在其中。
您幾乎只需要在前端代碼中就可以完成這個網頁應用程式至此程度的開發。

添加用户管理功能

接下来,我们将在这个应用程序中添加用户管理功能,只允许登录的用户进行投稿,并显示投稿者的名称。
首先,我们会在Amplify中添加用户认证功能。
只需要执行amplify add auth命令即可。

% amplify add auth

Do you want to use the default authentication and security configuration? Default configuration
How do you want users to be able to sign in? Username
Do you want to configure advanced settings? No, I am done.

当功能被添加后,将直接推送至AWS。
可能会有一些问题被询问,但暂时保持默认设置即可。

% amplify push

以上的工作已经在后端实现了用户认证功能。

根据用户登录功能对前台进行修正。

//...

<amplify-authenticator>
      <v-text-field
        v-model="form.comment"
        label="コメント"
        placeholder="ここにコメントを書きましょう"
        outlined
        class="mx-auto"
        append-icon="mdi-check-bold"
        style="max-width: 100%; box-sizing: border-box;"
        @keydown="onEnter"
        @click:append="createChat"
      ></v-text-field>
</amplify-authenticator>

//...
undefined

确认

请尝试创建一个账户并登录。我相信你可以成功登录并开始使用这个应用。

为API添加身份验证

在外观上,登录功能已被实现,但并没有将认证添加到API本身,所以直接访问终点等操作将导致数据的读写。
因此,我们将修改API设计,使其与此次添加的认证功能关联起来,只有已登录用户才能访问终点。
不过,只需要在schema.graphql中进行少量的添加。

type Chat @model @auth(rules: [{ allow: owner, operations: [create, delete, update] }]) {
  id: ID!
  comment: String!,
  owner: String
}

我已添加 @auth(rules: [{ allow: owner, operations: [create, delete, update] }])。将使用保存在owner字段中的用户名进行身份验证(用户名将自动保存),并允许执行create、delete和update操作。这样一来,Chat模型将实现以下状态:任何用户登录后都可以读取数据,但只有创建者才能写入数据。参考链接:https://docs.amplify.aws/cli/graphql-transformer/directives#auth

将此推送到AWS方面。

% amplify push

如果发生错误

如果在这里发生错误(按照上述的方法操作会导致错误),则需要进行相应的处理。
您必须将API更新为与auth相匹配。
我们将使用AWS的Cognito来进行API认证的更新。
使用amplify update api命令来更新API设置。

% amplify update api

? Please select from one of the below mentioned services: GraphQL
? Select from the options below Walkthrough all configurations
? Choose the default authorization type for the API Amazon Cognito User Pool #Cognito User Pool を選択してください。
Use a Cognito user pool configured as a part of this project.
? Do you want to configure advanced settings for the GraphQL API No, I am done.

等待一段时间AWS的设置将被更新。
然后,再一次操作。

% amplify push

那么,GraphQL的配置将被推送。
对于端点的身份验证配置也已完成。

另外,通过这个设置,DB将会保存所有者的姓名。
我认为,当您发表评论时,聊天工具中的发布者姓名也会显示出来。

修正成当您登录时,将会以列表形式呈现(随后在实时更新)。

由于针对终端点进行了身份验证设置,因此会出现一些不便。

    以登出状态打开屏幕(在此状态下无法读取列表)。进行登录。(可以读取列表了,但因为页面已经生成,所以需要刷新页面才能显示列表)。

当我登录时,我会从API中读取列表的处理,并且之后会进行实时更新。

<script>
import { API, graphqlOperation } from 'aws-amplify'
//...
import { onAuthUIStateChange } from '@aws-amplify/ui-components'

//...
  data() {
    return {
//...
      username: '',
//...
    }
  },

  created() {
    this.getChatList()
    // this.subscribe()

    // 追加
    onAuthUIStateChange((authState, authData) => { // ログインステータスが変化したとき
      if (authState === 'signedin') { // ログインした場合
        this.username = authData.username
        this.getChatList() // 一覧呼び出し
        this.subscribe() // GraphQL Subscription
      } else {
        this.items = [] // ログアウトしたときなどは一覧を削除
      }
    })
  },

  methods: {
    //...

    // ユーザー名を渡すように修正
    subscribe() {
        API.graphql(
          graphqlOperation(onCreateChat, { owner: this.username })
        ).subscribe({ 
        next: (eventData) => {
          // コメントが送信されて追加されたとき、送信内容を一覧に追加
          const chat = eventData.value.data.onCreateChat // データを読み込み
          if (this.items.some((item) => item.comment === chat.comment)) return // すでに表示されているデータは無視
          this.items = [...this.items, chat] // 新しいデータを追加
        },
      })
    },
  },

// ...
</script>

我已经设置了一个事件来监视登录状态。

这样一来,创建工作就全部完成了。
chat.vue 文件就变成了这个样子。
另外,我还顺便实现了注销按钮(amplify-sign-out)。

<template>
  <div style="max-width: 800px;">
    <amplify-authenticator>
      <v-text-field
        v-model="form.comment"
        label="コメント"
        placeholder="ここにコメントを書きましょう"
        outlined
        class="mx-auto"
        append-icon="mdi-check-bold"
        style="max-width: 100%; box-sizing: border-box;"
        @keydown="onEnter"
        @click:append="createChat"
      ></v-text-field>
    </amplify-authenticator>

    <v-card v-for="(item, index) in items" :key="index" tile>
      <v-list-item two-line>
        <v-list-item-content>
          <v-list-item-title>{{ item.comment }}</v-list-item-title>
          <v-list-item-subtitle>by: {{ item.owner }}</v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </v-card>

    <v-card>
      <amplify-sign-out v-if="logoutBtn"></amplify-sign-out>
    </v-card>
  </div>
</template>
<script>
import { API, graphqlOperation } from 'aws-amplify'
import { onAuthUIStateChange } from '@aws-amplify/ui-components'
import { createChat } from '~/src/graphql/mutations'
import { listChats } from '~/src/graphql/queries'
import { onCreateChat } from '~/src/graphql/subscriptions'

export default {
  data() {
    return {
      form: {
        comment: '',
      },
      items: [],
      logoutBtn: false,
      username: '',
    }
  },
  created() {
    this.getChatList()

    onAuthUIStateChange((authState, authData) => {
      if (authState === 'signedin') {
        this.username = authData.username
        this.getChatList()
        this.subscribe()
        this.logoutBtn = true
      } else {
        this.items = []
        this.logoutBtn = false
      }
    })
  },
  methods: {
    async createChat() {
      const comment = this.form.comment
      if (!comment) return
      const chat = { comment }
      await API.graphql({
        query: createChat,
        variables: { input: chat },
      })
      this.form.comment = ''
    },
    onEnter(event) {
      // エンターキーで送信
      if (event.keyCode !== 13) return
      this.createChat()
    },
    async getChatList() {
      // コメント取得メソッド
      const chatList = await API.graphql({
        query: listChats,
      })
      this.items = chatList.data.listChats.items
    },
    subscribe() {
      API.graphql(
        graphqlOperation(onCreateChat, { owner: this.username })
      ).subscribe({
        next: (eventData) => {
          const chat = eventData.value.data.onCreateChat
          if (this.items.some((item) => item.comment === chat.comment)) return // 重複しないように
          this.items = [...this.items, chat]
        },
      })
    },
  },
}
</script>

undefined

部署

最后,我们将使用Amplify进行托管。

% amplify add hosting

? Select the plugin module to execute Hosting with Amplify Console 
? Choose a type Manual deployment
% amplify publish

等待一段时间后即可完成发布。
显示URL后,请尝试访问。
瞬间,网络应用程序已创建完成。

undefined

因为我们已经花了那么多功夫来创建聊天工具,接下来可以尝试在密聊窗口或者另一个浏览器中同时进行聊天,这样可以体验到同时扮演两个角色的乐趣。这种简单操作能带来的惊喜和满足感,也能让你感受到独自聊天时的那份微妙的寂寞。

删除

既然已经制作了,但是将其保留在AWS上也不太好,所以也需要删除方法。

% amplify delete

你可以通过删除来实现。很简单吧。
另外,在进行以下访问限制等操作后,尝试一段时间进行各种试验也很有趣。

填补

你可以在AWS的控制台上通过AWS Amplify访问,并且可以通过[访问控制]选项来进行基本认证的访问限制。

用户开启认证后订阅出现的错误消息。

因为在很久以后重新调整时,发现在用户认证下无法正常运行subscribe,所以我进行了代码修正。(上述为修复后的代码)

Uncaught {provider: AWSAppSyncRealTimeProvider...
"Connection failed: {"errors":[{"message":"Variable 'owner' has coerced Null value for NonNull type 'String!'"}]}"

Nuxt的错误?

这次使用Nuxt时,在控制台出现错误。(可能与Vuetify相关)
‘v-content’已弃用,请改用’v-main’
虽然与主题无关,只是练习,可以忽略,但如果有疑问,
将layouts/default.vue中的v-content更改为v-main即可解决问题。
https://github.com/vuetifyjs/vuetify/issues/11634

可以参考

我这次参考的网站:
https://docs.amplify.aws/start/q/integration/vue
https://docs.amplify.aws/cli/graphql-transformer/directives#using-modular-imports
https://ja.nuxtjs.org/
https://vuetifyjs.com/ja/
https://qiita.com/fkymnbkz/items/fa7cd15de1039e62074e
https://github.com/aws-amplify/amplify-cli/issues/3480
https://github.com/vuetifyjs/vuetify/issues/11634

https://qiita.com/respectakagikun/items/0b976b12ddb34f027190

bannerAds