Apollo Clientもくもく会 2019.2.14@AWSオフィス

2019.02.14

はじめに

Apollo Clientを勉強していて、おそらくデータの読み出しと監視を行なっているreadQueryメソッドとwatchQueryメソッドが出てきたので、公式ドキュメントを読んで概要を把握する。

readQueryについてApollo Clientの公式ドキュメントを読んで概要を把握する

Tries to read some data from the store in the shape of the provided GraphQL query without making a network request. This method will start at the root query. To start at a specific id returned by dataIdFromObject use readFragment.

(意訳)ネットワーク通信を行わずに、GraphQLのクエリの形式でストアからデータ取得する。このメソッドはroot queryからスタートする(訳注:現時点では意味不明...)。dataIdFromObjectで返されるIDから始めるには、readFragmentを利用する。

通信せずにキャッシュからデータを読み出すメソッドとのこと。リストのような構造をしているデータを先頭から読み込むイメージ?readFragmentを使うことで位置を指定してデータを読み込めるということ?

後ほど実際に動かしながら検証したい。

watchQueryについてApollo Clientの公式ドキュメントを読んで概要を把握する

watchQuery(options) - Apollo Client

This watches the cache store of the query according to the options specified and returns an ObservableQuery. We can subscribe to this ObservableQuery and receive updated results through a GraphQL observer when the cache store changes.

(意訳)オプションを指定してキャッシュストアを監視しObservableQueryを返す。ObservableQueryを購読することで、キャッシュストアが変更されたときに更新情報を受け取ることができる。

キャッシュストアの変更を監視することができるということは、Optimistic Responseの内容もここで受け取ることができるかもしれない。

Note that this method is not an implementation of GraphQL subscriptions. Rather, it uses Apollo's store in order to reactively deliver updates to your query results.

(意訳)このメソッドはGraphQLのSubscriptionの実装ではないことに注意。むしろ、クエリの結果をリアクティブに伝播させるためにストアにアクセスする。

GraphQLサーバーからプッシュされる情報を購読するものではないとのこと。

For example, suppose you call watchQuery on a GraphQL query that fetches a person's first and last name and this person has a particular object identifer, provided by dataIdFromObject. Later, a different query fetches that same person's first and last name and the first name has now changed. Then, any observers associated with the results of the first query will be updated with a new result object.

(意訳)一意の識別子をもつユーザーの氏名を取得するようなGraphQLのqueryをwatchQueryを呼ぶケースを想定する。後に、他のクエリで同じユーザーの氏名を取得したとして、その時点で取得するユーザーの氏名は変更されているとする。そのとき、最初のクエリ結果を購読しているObserverには新しいオブジェクトが流れてくる。

watchQueryしておけば、変更されたときにUIの各部分がリアクティブに書き変わるということらしい。

Note that if the cache does not change, the subscriber will not be notified.

(意訳)キャッシュが変更されなければ、変更は通知されないことに注意。

基本的なクエリを試す準備をする

Angularアプリケーションを作成する

Angular 7でテスト用のアプリケーションを生成して、Apollo ClientをラップしたAWSAppSyncSDKを使ってreadQuerywatchQueryあたりを試してみる。



$ ng new AngularApolloSample

$ cd AngularApolloSample

# AWS AppSync JavaScript SDKをインストール
$ npm install --save aws-appsync graphql-tag

ディレクトリ構造は以下のようになった。



$ tree src
src
├── app
│   ├── app.component.css
│   ├── app.component.html
│   ├── app.component.spec.ts
│   ├── app.component.ts
│   └── app.module.ts
├── assets
├── environments
│   ├── environment.prod.ts
│   └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.app.json
├── tsconfig.spec.json
└── typings.d.ts

AppSync APIを作成する

AppSyncのマネジメントコンソールから作成。適当にPostモデルを作成する。

Apollo ClientのwatchQuery・readQueryについて調べる

Apollo ClientのwatchQuery・readQueryについて調べる

AngularプロジェクトでAppSyncにつなぐ



ngOnInit() {
  this.client = new AWSAppSyncClient({
    url: 'https://XXXXXXXXXXX.appsync-api.ap-northeast-1.amazonaws.com/graphql',
    region: 'ap-northeast-1',
    auth: {
      type: AUTH_TYPE.API_KEY,
      apiKey: 'XXXXXXXXXXXXX',
    }
  });
}

ボタンを押したらクエリを投げる処理を書く



import { Component, OnInit } from '@angular/core';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import { v4 as uuid } from 'uuid';
import * as queries from '../graphql/queries';
import * as mutations from '../graphql/mutations';

interface Post {
  id: string;
  content: string;
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  client;
  content = '';
  posts: Post[] = [];
  posts2: Post[] = [];

  ngOnInit() {
    this.client = new AWSAppSyncClient({
      url: 'https://XXXXXXXXXXXXXXXXXXXXX.appsync-api.ap-northeast-1.amazonaws.com/graphql',
      region: 'ap-northeast-1',
      auth: {
        type: AUTH_TYPE.API_KEY,
        apiKey: 'XXXXXXXXXXXXXXXXXXXXX',
      }
    });

    this.setWatchQuery();
    this.load();
  }

  onCreateClicked() {
    this.client.hydrated().then(client => {
      client.mutate({
        mutation: mutations.createPost,
        variables: {
          input: {
            content: this.content,
          }
        },
        optimisticResponse: () => ({
          createPost: {
            id: uuid(),
            content: this.content,
            __typename: 'Post'
          }
        }),
      }).then(() => {
        this.content = '';
      });
    });
  }

  load() {
    this.client.hydrated().then(client => {
      client.query({
        query: queries.listPosts,
        fetchPolicy: 'network-only',
      }).then(data => {
        this.posts = data.data.listPosts.items;
      });
    });
  }

  setWatchQuery() {
    this.client.hydrated().then(client => {
      client.watchQuery({
        query: queries.listPosts,
        fetchPolicy: 'cache-and-network',
      }).subscribe(data => {
        console.log('subscribe', data);
        this.posts2 = data.data.listPosts.items;
      });
    });
  }
}

今日やったこと

  • readQuery・watchQueryについてのドキュメントを和訳して理解
  • テスト用Angularプロジェクトを作成
  • テスト用AppSync APIを作成
  • AngularアプリからAppSyncに対してMutation・Queryを実行する部分を実装
  • AngularアプリでwatchQueryを利用してみる
  • クエリを発行するときに指定するfetchPolicyで動作が異なることはわかった
  • 若干理解できたreadQuery・watchQueryがChatQLでどう呼ばれているかを見ることができた

課題点

  • fetchPolicyに指定できるタイプによって動作がどう変わるかがわからない。
  • readQuerywatchQueryのユースケースがわからない。

今日学んだこと

  • apollo-link-stateを使うと、Redux等を使って行なっていたクライアントの状態管理をApollo Clientに統合することができる。
  • Apollo Clientの公式Docに詳細な説明が載っていないメソッドはGitHubの関数定義部分を参照すると載っている場合がある。
  • VSCodeの拡張機能を使うとReactのコンポーネントなどがすぐ作れて非常に便利。