BLOG ブログ


2020.07.13 TECH

Amplify で開発してみる #1 / API (GraphQL) その1 テーブル一つの実装

こんにちは、tacckです。

既報のとおり、社内の座席管理システム「せきとりくん」というものを作り、無事にリリースしました。🎉

こちらは AWS Amplify を使って作成しています。
API (GraphQL) の機能を活用できると手軽に Serverless なシステムを作れるようになるので、大変良いですね。

そんなわけで、今回の記事を含め全四回(予定)をかけて API (GraphQL) を実際に使った開発の流れを見ていきたいと思います。

Amplify で開発してみるシリーズ

* Amplify で開発してみる #1 / API (GraphQL) その1 テーブル一つの実装 (今回)

* Amplify で開発してみる #2 / API (GraphQL) その2 テーブル連携した実装

* Amplify で開発してみる #3 / Authentication と API (GraphQL) を連携した実装

* Amplify で開発してみる #4 / Subscription を使った API (GraphQL) のリアルタイムデータ連携

開発内容

このシリーズでは、ミーティングやLTなど複数の人が集まった時に、コメントを気軽に投稿できるシステムを作っていきます。

現在は、リモートでの働く割合も増えてきていると思いますが、リモートのミーティングで誰でも気軽に意見を言える、後から見直すこともできる、といった活用もできるのではないかと思います。

アプリケーション名は、"Do you have any comment?" (コメントはありますか?) をもじって、
Comment Any としておきます。

機能

おおよそ、下記の機能を備えたシステムとして開発を進めたいと思います。

  • イベント
    • イベント一覧表示 (全ユーザー)
    • イベントの有効化・無効化 設定 (自分のイベントのみ)
  • イベント詳細
    • イベント名表示
    • コメント一覧表示
    • コメントの削除(自分のイベントのみ)
  • コメント
    • 投稿
    • Like

とはいえ、今回は Amplify にフォーカスしたいので、システムのUIについては私が Vue.js でモックを作成済みです。

GitHub tacck/comment-any baseブランチ

今回は、これをベースに「コメント」に関する機能を実装していきましょう。

開発

ソースコード準備

ソースコードは、私の方で作成したモックをベースに話を進めていきます。

Node.js と Yarn は下記のバージョンで進めていきます。

$ node -v
v12.4.0
$ yarn -v
1.21.1

ソースコードを GitHub から clone して、開発の準備を進めていきましょう。

$ git clone https://github.com/tacck/comment-any.git
$ cd comment-any
$ git checkout -b base origin/base
$ yarn install

まず、この状態で動作確認してみます。 yarn serve 後に、 http://localhost:8080 へアクセスしてみてください。
下記のような画面となれば OK です。

Comment Any Top

Amplify 初期設定

では、 Amplify の設定に入っていきましょう。
まずは、初期設定です。

$ amplify init
Scanning for plugins...
Plugin scan successful
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project commentany
? Enter a name for the environment dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building javascript
Please tell us about your project
? What javascript framework are you using vue
? Source Directory Path:  src
? Distribution Directory Path: dist
? Build Command:  yarn build
? Start Command: yarn serve
Using default provider  awscloudformation

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default
Adding backend environment dev to AWS Amplify Console app: XXXXXXXXXXXXXX
⠧ Initializing project in the cloud...

(snip)

CREATE_COMPLETE amplify-commentany-dev-XXXXXX AWS::CloudFormation::Stack Thu Jun 25 2020 17:49:51 GMT+0900 (GMT+09:00) 
CREATE_COMPLETE DeploymentBucket              AWS::S3::Bucket            Thu Jun 25 2020 17:49:48 GMT+0900 (GMT+09:00) 
✔ Successfully created initial AWS cloud resources for deployments.
✔ Initialized provider successfully.
Initialized your environment successfully.

Your project has been successfully initialized and connected to the cloud!

Some next steps:
"amplify status" will show you what you've added already and if it's locally configured or deployed
"amplify add <category>" will allow you to add features like user login or a backend API
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify console" to open the Amplify Console and view your project status
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

Pro tip:
Try "amplify add api" to create a backend API and then "amplify publish" to deploy everything

$

前回と同様、行の色が薄くなっている箇所が、入力の必要なところになります。基本的に書かれてある通りに進めてしまって問題ありません。
(詳しくわかる方は、自由にカスタマイズしてみてください。)

Amplify Framework パッケージ追加

Amplify Framework と、 Vue.js 用のパッケージを追加します。

$ yarn add aws-amplify @aws-amplify/ui-vue

追加したら、下記のように src/main.js ファイルを更新します。

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify'

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

Amplify.configure(aws_exports)

Vue.config.productionTip = false

new Vue({
  router,
  store,
  vuetify,
  render: h => h(App),
}).$mount('#app')

API (GraphQL) 追加

Amplify API (GraphQL) 機能を、プロジェクトに追加します。

$ amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: commentany
? Choose the default authorization type for the API API key
? Enter a description for the API key: 
? After how many days from now the API key should expire (1-365): 365
? 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? No

The following types do not have '@auth' enabled. Consider using @auth with @model
         - Todo
Learn more about @auth here: https://docs.amplify.aws/cli/graphql-transformer/directives#auth


GraphQL schema compiled successfully.

Edit your schema at /Users/[YOUR_DIRECTORY]/comment-any/amplify/backend/api/commentany/schema.graphql or place .graphql files in a directory at /Users/[YOUR_DIRECTORY]/comment-any/amplify/backend/api/commentany/schema
Successfully added resource commentany locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud

$

色々と入力を促されますが、記載を参考に進めていけば問題ありません。

Schema 設定

実際にデータを保存する形式を GraphQL の Schema として定義します。

type が、いわゆる RDB のテーブルと同じような意味合いになります。

type Comment @model {
  id: ID!
  eventId: ID!
  comment: String!
  likes: Int!
  name: String
  updatedAt: AWSDateTime
}

type 内の各項目は、それぞれ下記のような意味合いとなります。

項目名説明
idID!自身のユニークさを示す情報
eventIdID!(後々) イベントとの連携を示す情報
commentString!コメント内容を保持
likesInt!Like 数を保持
nameString投稿したユーザー名を保持 (システム内では未使用)
updatedAtAWSDateTime更新日時 ( AppSync による自動更新)

より詳しい内容は、オフィシャルのドキュメントを参照してみてください。

API (GraphQL) Overview

AWS AppSync でのスカラー型

ここまでできたら、一旦 AWS へ情報をプッシュしておきましょう。

$ amplify push
✔ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name | Operation | Provider plugin   |
| -------- | ------------- | --------- | ----------------- |
| Api      | commentany    | Create    | awscloudformation |
? Are you sure you want to continue? Yes

The following types do not have '@auth' enabled. Consider using @auth with @model
         - Comment
Learn more about @auth here: https://docs.amplify.aws/cli/graphql-transformer/directives#auth


GraphQL schema compiled successfully.

Edit your schema at /Users/[YOUR_DIRECTORY]/comment-any/amplify/backend/api/commentany/schema.graphql or place .graphql files in a directory at /Users/[YOUR_DIRECTORY]/comment-any/amplify/backend/api/commentany/schema
? 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
⠏ Updating resources in the cloud. This may take a few minutes...

(snip)

UPDATE_COMPLETE                     amplify-commentany-dev-XXXXXX AWS::CloudFormation::Stack Thu Jun 25 2020 18:07:58 GMT+0900 (GMT+09:00) 
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS amplify-commentany-dev-XXXXXX AWS::CloudFormation::Stack Thu Jun 25 2020 18:07:58 GMT+0900 (GMT+09:00) 
CREATE_COMPLETE                     apicommentany                 AWS::CloudFormation::Stack Thu Jun 25 2020 18:07:55 GMT+0900 (GMT+09:00) 
✔ Generated GraphQL operations successfully and saved at src/graphql
✔ All resources are updated in the cloud

GraphQL endpoint: https://XXXXXXXXXXXXXXXXXXXXXXXXXX.appsync-api.ap-northeast-1.amazonaws.com/graphql
GraphQL API KEY: da2-XXXXXXXXXXXXXXXXXXXXXXXXXX

$ 

コメント機能実装

準備が整ったので、モックに肉付けをしていきましょう。

実装した内容はこちらのブランチにあるので、つまづいてしまったら確認してみてください。

GitHub Comment機能追加ブランチ

import 追加

これから実装を行なうにあたって、必要なライブラリを import しておきましょう。
src/views/EventDetail.vue<script> タグの直下に、下記のように追加してください。

import { API, graphqlOperation } from 'aws-amplify'
import { listComments } from '@/graphql/queries'
import {
  createComment,
  updateComment,
  deleteComment,
} from '@/graphql/mutations'

コメント一覧表示

  created: async function() {
    this.linkUrl = location.href

    const items = await API.graphql(graphqlOperation(listComments)).catch(err =>
      console.error('listComments', err),
    )
    this.comments = items.data.listComments.items
  },

4〜6行目で、コメント一覧を呼び出しています。取得した内容を this.comments へ格納することで、結果を画面に反映することができます。
( created 内に実装)

listComments が GrqphQL のクエリとなっています。詳しい内容は、 src/graphql/queries.js にあります。 (先ほどの amplify push 時に生成されたものです。)

export const listComments = /* GraphQL */ `
  query ListComments(
    $filter: ModelCommentFilterInput
    $limit: Int
    $nextToken: String
  ) {
    listComments(filter: $filter, limit: $limit, nextToken: $nextToken) {
      items {
        id
        eventId
        comment
        likes
        name
        updatedAt
        createdAt
      }
      nextToken
    }
  }
`;

こういった基本的なクエリも Amplify が自動生成してくれるので、かなり簡単に GraphQL を利用することができます。

コメント追加

    addComment: async function(inputComment) {
      if (!inputComment || inputComment.length <= 0) {
        return
      }

      const comment = {
        eventId: this.event.id,
        name: this.loginUserName,
        comment: inputComment,
        likes: 0,
      }

      const item = await API.graphql(
        graphqlOperation(createComment, { input: comment }),
      ).catch(err => console.error('createComment', err))
      const savedComment = item.data.createComment

      this.comments.push(savedComment)

      this.scrollToId(savedComment.id)
      this.pushUpdatedIds(savedComment.id)
    },

このメソッドが、コメントの投稿ボタンをクリックした時に呼ばれます。引数の inputComment に、投稿するコメントが入ってくる感じですね。

13〜15行目が GraphQL を使ってコメントを保存しているところです。ここも、すでに定義済みの createComment クエリを使うことで、簡単に保存処理が実装できています。

また、ここで保存した結果の savedComment を使うことで、 id がきちんと生成された状態になります。

Like 追加 (コメント更新)

    addLike: async function(elementId) {
      const comment = this.getComment(elementId)
      if (comment === null) {
        return
      }

      const beforeIndex = this.getCommentIndex(elementId)

      const input = {
        id: comment.id,
        likes: comment.likes + 1,
      }

      const item = await API.graphql(
        graphqlOperation(updateComment, { input: input }),
      ).catch(err => console.error('updateComment', err))
      const savedComment = item.data.updateComment

      comment.likes = savedComment.likes
      comment.updatedAt = savedComment.updatedAt
      const afterIndex = this.getCommentIndex(elementId)

      if (beforeIndex > afterIndex) {
        this.scrollToId(comment.id)
        this.pushUpdatedIds(comment.id)
      }
    },

ここは Like ( likes ) を 1 増やしてコメントを更新しています。

追加などと同じように、14〜16行目だけで更新処理が実装できています。

更新後の情報が savedComment で返ってくるので、その内容を表示に反映させます。

コメント削除

    deleteComment: async function(elementId) {
      const index = this.comments.findIndex(item => item.id === elementId)
      if (index < 0) {
        return
      }

      const comment = this.getComment(elementId)
      const input = {
        id: comment.id,
      }

      await API.graphql(
        graphqlOperation(deleteComment, { input: input }),
      ).catch(err => console.error('deleteComment', err))

      this.comments.splice(index, 1)
    },

ここは、コメントの削除ボタンがクリックされた場合に呼ばれます。

ここでも、定義済みの GraphQL クエリである deleteComment を使って削除処理を実行しています。(12〜14行目)

各機能のコードの詳細は、こちらを確認してみてください。

GitHub Comment機能追加ブランチ

どうにもうまくできない場合は、下記の実装差分を見て対応の漏れが無いかも見てください。

GitHub Comment機能 実装差分

うまく実装できれば、画面上でコメントの追加・削除・ Like での並び替え、が動作します。

まとめ

API (GraphQL) の機能を、とても簡単に組み込むことができました。

基本的な GraphQL のクエリは Amplify が自動生成してくれるので、まずは細かいことを気にせずにすぐに使い始めることができるようになります。

「ちょっとデータを読み書きしたいけど、 RDB を立てるほどでもないしなぁ。」という方、是非 Amplify を試してみてください。

次回は、イベントの type を追加し、今回作成したコメントと連携を取れるようにしてみたいと思います。


tacck
ライター名:tacck
元 技術推進Group Group Leader

現在は株式会社ノースディテールを離れて、
エバンジェリストとして技術啓蒙や勉強会の開催、各種プロジェクトに参画しています。
機会があれば、ノースディテールでのプロジェクトに参加できればと思っています。

言語は問わずに対応しますが、心はPHPer。
フロントエンド・バックエンド・インフラ・スマホアプリなどを、
「垣根を超えて」どう作るか、を考えるのが好きです。

好きなフィギュアスケートの技はスプレッド・イーグル。

主な記事一覧へ

一覧に戻る


LATEST ARTICLE 最新の記事

CATEGORY カテゴリー