North Detail / ノースディテール

BLOG ブログ

ブログ
CATEGORY
TECH

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

こんにちは、tacckです。

シリーズの第二回目です。

前回の記事から引き続き、 Comment Any の開発を進めていきましょう。

今回は、イベント関連の機能を開発します。

イベントの一覧表示、イベントの追加、選択したイベントへ遷移(前回実装分のコメント機能との連携)、イベントの有効化・無効化、といったところですね。

前回までのコードはこちらです。

GitHub Comment機能追加ブランチ

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

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

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

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

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

開発

Schema 設定

今回はイベントに関する type の設定を追加し、このイベントと前回追加したコメントの連携を設定してみましょう。

type Event
  @model
{
  id: ID!
  name: String
  active: Boolean!
  comments: [Comment] @connection(keyName: "listCommentByEventId", fields: ["id"])
  createdAt: AWSDateTime
  owner: String
}

type Comment
  @model
  @key (name: "listCommentByEventId", fields: ["eventId"], queryField: "listCommentByEventId")
{
  id: ID!
  eventId: ID!
  comment: String!
  likes: Int!
  name: String
  updatedAt: AWSDateTime
}

行の色が薄くなっている箇所が、追加した箇所になります。

1~10行目は、イベントの型となります。

項目名説明
idID!自身のユニークさを示す情報
nameStringイベント名を保持
activeBoolean!イベントの有効・無効を保持
comments[Comment]このイベント内のコメント(複数)と連携
createdAtAWSDateTime作成日時 ( AppSync による自動作成)
ownerString作成したユーザーID (AppSync による自動追加/今回未使用)

この comments の後ろにある @connection ディレクティブと、 type Comment の方に追加した14行目の @key ディレクティブが連携の要となります。

@connectionkeyName フィールドの値 (今回だと listCommentByEventId ) の @key に対して、 Event 自身の fields フィールドの値 (今回だと ["id"] ) を使ったクエリを発行し、その結果を comments として一緒に保持する、という動作をします。

つまり、イベントの検索クエリを実行すれば、そのまま必要なコメントも連携して取得することが可能となります。

一般的な REST API + RDB だと、イベントとコメントを別々の API として呼び出すか、 RDB の SQL の方で JOIN 句を使った API を用意するか、、となります。

データ取得の効率としては後者が必須ですが、 Amplify API (GraphQL) であれば、 JOIN 句の変わりに @connection@key を連携させることで効率よくデータ取得を行なうことができるようになるのです。

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

API (GraphQL) Directives @connection

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

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

Current Environment: dev

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

The following types do not have '@auth' enabled. Consider using @auth with @model
         - Event
         - 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 update code for your updated GraphQL API Yes
? Do you want to generate GraphQL statements (queries, mutations and subscription) based on your schema types?
This will overwrite your current graphql queries, mutations and subscriptions Yes
⠹ Updating resources in the cloud. This may take a few minutes...

(snip)

UPDATE_COMPLETE amplify-commentany-dev-174901 AWS::CloudFormation::Stack Thu Jul 02 2020 17:40:41 GMT+0900 (GMT+09:00) 
UPDATE_COMPLETE apicommentany                 AWS::CloudFormation::Stack Thu Jul 02 2020 17:40:40 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 Event機能追加ブランチ

import 追加

今回も、必要なライブラリの import からです。

今回は src/views/Event.vue<script> タグの直下に、下記のように追加してください。

import { API, graphqlOperation } from 'aws-amplify'
import { listEvents } from '@/graphql/queries'
import { createEvent, updateEvent } from '@/graphql/mutations'

イベント一覧表示

  created: async function() {
    const items = await API.graphql(graphqlOperation(listEvents)).catch(err =>
      console.error('listEvents', err),
    )
    this.events = items.data.listEvents.items
  },

2〜4行目で、イベントの一覧を呼び出しています。取得した内容を this.events へ格納することで、結果を画面に反映することができます。

この辺りは、前回のコメント一覧表示とまったく同じやりかたですね。

こちらでは、 listEvents のクエリを使ってイベント一覧を取得しています。

イベント追加

    addEvent: async function() {
      if (!this.newEventName || this.newEventName.length <= 0) {
        return
      }

      const input = {
        name: this.newEventName,
        active: true,
        owner: 'me',
      }

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

      this.events.push(savedEvent)

      this.newEventNameDialog = false
      this.newEventName = ''
    },

イベント追加ボタンをクリックした後のダイアログに入力されたイベント名を使って、新たにイベントを追加するメソッドとなります。

12〜14行目が GraphQL を使ってイベントを保存しているところです。前回同様、すでに用意されている createEvent クエリを使うことで、簡単にイベントを保存できています。

イベントロック (イベント更新)

    activeSwitch: async function(event) {
      const index = this.events.findIndex(item => item.id === event.id)
      if (index < 0) {
        return
      }

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

      this.events.splice(index, 1, savedEvent)
    },

イベントのロック・アンロックボタンをクリックしたときに、呼ばれるメソッドです。

こちらも今まで通り、7〜9行目で更新処理を行なっています。

ちなみに、このメソッド内では状態変更処理がありませんが、 component である src/components/EventItem.vue の方で処理しているからです。

イベント削除

今回は削除機能は実装しません。

今まで見てきた通り実装はとても簡単にできると思いますので、みなさんで追加機能として実装してみてください。

コメントとの連携

イベントの一覧が表示できるようになったので、イベントとコメントの連携を実装していきましょう。

具体的には、「イベント一覧から特定のイベントをクリックすると、イベント詳細(コメント一覧)ページへ遷移し、そのイベント内で追加されたコメント一覧が表示される。」ということを実現していきます。

まずは、イベント詳細への遷移から。

    <v-row v-for="(event, index) in sortedEvents" :key="index">
      <EventItem
        :event="event"
        :isEventOwner="isEventOwner(event.owner)"
        @activeSwitch="activeSwitch"
      ></EventItem>
    </v-row>

これは、イベント一覧で各イベント名を component 化した src/components/EventItem.vue を使って表示している実装です。

特定のイベントをクリックした場合の処理は、この src/components/EventItem.vue の中に存在します。

    <v-card
      :to="{ name: 'EventDetail', params: { eventId: event.id } }"
      :outlined="!event.active"
    >

このように、 Vuetify のカードを使って表示を整えています。

ここで to 属性を設定することで、クリックされた場合にどこに遷移するか、が決まります。

ここは、 EventDetail (イベント詳細) ページに eventId を持って遷移するすることになります。

これで src/views/EventDetail.vue にうまく遷移するところまできたので、遷移後の処理を追加・修正していきましょう。

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

import は、上記のように2行目を getEvent だけにします。 Schema 設定 で説明したように、イベントとそれに連携したコメントを一度に取得できるからです。

実際の使い方が、下のようになります。

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

    const item = await API.graphql(
      graphqlOperation(getEvent, { id: this.eventId }),
    ).catch(err => console.error('getEvent', err))
    this.event = item.data.getEvent
    this.comments = this.event.comments.items
  },

4〜6行目でイベント取得処理を行なっています。

7行目でイベント情報を格納し、8行目で取得したイベント情報内のコメント一覧を取り出して格納しています。

これだけで、イベントとコメントの連携は実装完了です!

詳細は、こちらを確認してみてください。

GitHub Event機能追加ブランチ

また、細かい部分で端折っているところもあるので、うまくできない場合には、下記の実装差分も参考にしてみてください。
特にイベントのロック・アンロック時のボタン制御やコメントの表示などは Vue.js の実装内容となるので、今回の解説ではほとんど触れていません。そういったところのコードも、是非追いかけてみてください。

GitHub Event機能 実装差分

うまく実装できましたか?
うまくいけば、下記の動画のようにイベントの追加、コメントの連携、イベントのロック、といったものが動作するはずです。

まとめ

引き続き API (GraphQL) の機能が、とても簡単に扱えるのを見ていきました。

特にイベントとコメントのデータ連携は、思っていた以上に簡単に実現できたと思います。

type の連携が実装できるようになると、扱えるデータの幅が一気に広がりますし、それに伴ってシステムで実現できるものの幅も広がっていきます。

さらに実現できるものの幅を広げるために、次回は認証機能の導入と API との連携をやってみましょう。

tacck
WRITER:tacck
元 技術推進Group Group Leader

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

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

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

一覧へ

IS 501383 / ISO 27001