GraphQLを使ってシンプルな電子掲示板を作ってみる(6)

2020.6.21 12:00

前回に引き続き、一覧取得を作っていく。

投稿フォームから投稿してみる

GraphQLの定義

前回と同様に、 graphql-tag を使って一覧取得用のQueryを定義する。

src/views/Posts.vue
    ...
    import {PostFormData, PostModel} from '@/types'
    import {RawLocation} from 'vue-router'
    import Paginator from '@/components/Paginator.vue'
    import gql from 'graphql-tag'

+   const LIST_POSTS = gql`
+     query listPosts {
+       posts(order_by: {created_at: desc}) {
+         created_at
+         email
+         id
+         message
+         name
+         title
+         comments(order_by: {created_at: desc}) {
+           created_at
+           email
+           id
+           message
+           name
+         }
+       }
+     }
+   `

    const ADD_POST = (name?: string) => gql`
    ...

リクエスト処理

Vueのコンポーネントオプションにapolloプロパティが定義されているので、 ここに任意の名前のオブジェクトでクエリを追加することで、 自動的にdataに取得結果を詰めることができる。

src/views/Posts.vue
      ...
      @Component({
        components: {
          Paginator,
          Post,
          PostForm
+       },
+       apollo: {
+         posts: { // ここで指定した名前で参照できる
+           query: LIST_POSTS
+         }
        }
      })
      export default class Posts extends Vue {
        private q = ''
      ...

ダミーデータの削除

リクエストデータをひょうじできるようにしたので、 ダミーデータは削除しておく。

src/views/Posts.vue
      ...
      export default class Posts extends Vue {
        private q = ''
-       private posts: Array<PostModel> = [...Array(15).keys()]
-           .map(i => ({
-             id: i+1000000,
-             title: `Title of ${i}`,
-             message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce blandit sem at enim tincidunt, quis auctor dui tincidunt. In vitae sem ut massa dapibus lacinia sit amet ut ligula. Aenean sit amet nisl ut lectus rhoncus tempus. Morbi rhoncus viverra varius. Quisque ac libero scelerisque ligula molestie iaculis. Duis placerat, urna in ullamcorper laoreet, eros lorem egestas purus, nec vehicula justo quam vitae ante. Vivamus efficitur ligula et leo consequat bibendum. Proin ac vulputate libero, ac placerat dui. Nulla venenatis sem ut finibus rutrum. Suspendisse interdum ex orci, vel posuere massa consequat quis. Donec sit amet metus magna.',
-             name: `My name is ${i}`,
-             email: i % 2 === 0 ? 'dummy@example.com' : null,
-             created_at: dayjs().add(i, 'd').format('YYYY-MM-DDTHH:mm:ssZ'),
-             comments: [
-               {
-                 id: (i+1000000)*10+1,
-                 message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce blandit sem at enim tincidunt, quis auctor dui tincidunt. In vitae sem ut massa dapibus lacinia sit amet ut ligula. Aenean sit amet nisl ut lectus rhoncus tempus. Morbi rhoncus viverra varius. Quisque ac libero scelerisque ligula molestie iaculis. Duis placerat, urna in ullamcorper laoreet, eros lorem egestas purus, nec vehicula justo quam vitae ante. Vivamus efficitur ligula et leo consequat bibendum. Proin ac vulputate libero, ac placerat dui. Nulla venenatis sem ut finibus rutrum. Suspendisse interdum ex orci, vel posuere massa consequat quis. Donec sit amet metus magna.',
-                 name: `My name is Bob`,
-                 email: 'dummy@example.com',
-                 created_at: dayjs().add(i, 'd').format('YYYY-MM-DDTHH:mm:ssZ'),
-               },
-               {
-                 id: (i+1000000)*10+2,
-                 message: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce blandit sem at enim tincidunt, quis auctor dui tincidunt. In vitae sem ut massa dapibus lacinia sit amet ut ligula. Aenean sit amet nisl ut lectus rhoncus tempus. Morbi rhoncus viverra varius. Quisque ac libero scelerisque ligula molestie iaculis. Duis placerat, urna in ullamcorper laoreet, eros lorem egestas purus, nec vehicula justo quam vitae ante. Vivamus efficitur ligula et leo consequat bibendum. Proin ac vulputate libero, ac placerat dui. Nulla venenatis sem ut finibus rutrum. Suspendisse interdum ex orci, vel posuere massa consequat quis. Donec sit amet metus magna.',
-                 name: `My name is Alice`,
-                 email: null,
-                 created_at: dayjs().add(i, 'd').format('YYYY-MM-DDTHH:mm:ssZ'),
-               }
-             ]
-           }))
+       private posts: Array<PostModel> = []
      ...

キャッシュの更新

公式のインストールガイドに沿ってインストールした場合、 Apolloではクエリの結果をInMemoryCacheに保持するようにしている。

InMemoryCacheはデフォルトの設定ではデータの追加/削除時にメモリ内のデータを更新せず、 このままでは投稿しても表示が反映されないため、直接dataに追加するのではなく、 InMemoryCacheの値を更新することで、投稿後にdataが更新されるようにする。

src/views/Posts.vue
      ...
      createPost(value: PostFormData) {
        this.$apollo.mutate({
          mutation: ADD_POST(value.name),
-         variables: value
-       })
-       .then(({data: { insert_posts_one: post } }) => {
-         // 先頭に今回更新したデータを追加
-         this.posts.unshift(post)
-         // フォームデータをクリア
-         const postForm = this.$refs.postForm as PostForm
-         postForm.reset()
+         variables: value,
+         update: (cache, { data: { insert_posts_one: post } }) => {
+           // キャッシュの読み込み
+           const data = cache.readQuery({
+             query: LIST_POSTS
+           }) as {posts: PostModel[]}
+           // キャッシュデータの先頭に今回更新したデータを追加
+           data.posts.unshift(post)
+           // キャッシュの更新
+           cache.writeQuery({
+             query: LIST_POSTS,
+             data
+           })
+           // フォームデータをクリア
+           const postForm = this.$refs.postForm as PostForm
+           postForm.reset()
+         }
        })
      }
      ...

mutateのoptionsに指定できるupdateの関数オブジェクトの引数は

  1. キャッシュDAO
  2. 更新データ(thenの第一引数と同じ)

となっている。

基本的にはcacheに対し

  1. readQueryで対象のクエリを指定してキャッシュを読み込み
  2. 読みだしたデータを適切に加工し
  3. writeQueryでキャッシュを更新する

という流れになる。

デモ

ページを開くとバックエンドに登録した記事が表示されるようになり、 記事を投稿すると先頭に表示される。

再読み込みをしても投稿した記事が先頭に表示されている。

6-1

次回

次回は投稿された記事に返信してみる。

graphql

Copyright 2020 tkzwhr's tech notes. All Rights Reserved. Built with Gatsby.