Apollo Clientで始めるGraphQLと状態管理

## GraphQLとそのクライアントライブラリ「Apollo Client」の話です

---

GraphQLで開発を行っているチームに向けた社内勉強会用の資料です。

コードはボイラープレート化されていますが、GraphQLのメリットや全体像について理解を促進する目的で作成しました。

---

## Topic 💁

・GraphQLの特徴
・Apollo Client
・GraphQL Code Generator
・開発の流れ

---

## GraphQLの特徴

---

### GraphQLの特徴 - Schema

APIとクライアント間のインターフェースとして利用される

```graphql
type Post {
  id: String!
  created_at: DateTime!
}
```

---

### GraphQLの特徴 - Schema

クライアントからは
・request / responseの型情報
・データ取得関数の自動生成
として利用される。

--- 

### GraphQLの特徴 - Schema

・補完が効いて開発効率が上がる ✅
・型安全になり修正・リファクタ時の負荷が軽減される ✅

e.g. API変更の影響をざっくり知りたい

```bash
$ npm run lint:ts
```

---

### GraphQLの特徴 - RESTとの比較

・クライアントがレスポンスを組み立てる
・基本的にリソース=リゾルバ (エンドポイント単位でのサブリソースの管理が不要)

e.g. 記事ページのAPIにコメントを生やしたが、他ページでもコメントを取りたくなった


---

### GraphQLの特徴 - RESTとの比較

・クライアントファーストな仕組みと言える
・クライアントに複雑性が移動してきた背景を考えるとGraphQLの登場は自然な文脈
・学習・環境構築コストが高く、パフォーマンス出しにくいため使い所は見極めたい

---


## Apollo Client

---

### Apollo Client

・クライアント側のGraphQLライブラリ
・旧facebook社が開発したRelayと2大巨頭
・後発ながら機能が豊富で利用者が多く、Relayに比べて制約が弱め

https://www.apollographql.com/docs/react/why-apollo/

---

### Apollo Client - 状態管理

コンポーネントを跨いで利用したい状態をグローバルステートと呼び、その管理方法が「状態管理」と呼ばれている

状態管理で扱うデータの種類は大きく2つ
・API経由データ
・それ以外データ (共通モーダルの表示など)

---

### Apollo Client - 状態管理

Reduxなどを利用してコンポーネントを跨いで利用したいグローバルステートを管理するのがセオリー化している

下記のような課題がある (と感じている)
・ボイラープレートが多くて大変
・コード量が多くなると職人が必要

---

### Apollo Client - 状態管理

SWR, useQueryといったキャッシュ機構を持つライブラリが流行ってきた

前述の「API経由、それ以外データ」を識別子単位でメモリキャッシュしてくれる機能を持っている

--- 

### Apollo Client - 状態管理

apolloもキャッシュ機構を持っていて、更にオブジェクトごとに正規化してくれる機能がついている

```
// __typename x idを識別子としてユニーク性を担保
{ __typename: 'Post', id: 'uuid-1', name: 'something' },
{ __typename: 'Post', id: 'uuid-2', name: 'anything' }
```

--- 

### Apollo Client - 状態管理

下記のような挙動が可能
・ミューテーションで記事を更新する
・記事一覧のキャッシュが更新される
・記事一覧の表示が更新される

-> API経由データの更新時にグローバルステートの更新が不要になる 👏

---

### Apollo Client - 状態管理

それ以外データは「Reactive variables」という機能を利用して、従来通り自分で変更します。

---

### Apollo Client - 状態管理

```
// store.js
const flagVar = makeVar(false);

// component.jsx
const Component = () => {
  const flag = useReactiveVar(flagVar); // reactiveに変更を検知
  return <div onClick={() => flagVar(true)}>{flag}</div>;
}
```

---

## GraphQL Code Generator

---

### GraphQL Code Generator

下記を自動生成してくれるツール
・APIのレスポンス型
・データ取得のhooks

https://www.the-guild.dev/graphql/codegen/plugins/typescript/typescript-react-apollo

---

### GraphQL Code Generator

このとき自動生成されるコードは下記を参照している
・Schema
・オペレーションコード (クライアントからのリクエスト)

---

### GraphQL Code Generator - query(参照)

Schema
```
// ルート型
type Query {
  post: Post // フィールド(RESTのendpointに相当)
}
// オブジェクト型
type Post { id: String! title: String }
```

---

### GraphQL Code Generator - query(参照)

オペレーションコード
```
query PostPage { // オペレーションタイプ オペレーションネーム
  post { // フィールド
    id
    title
  }
}
```

---

### GraphQL Code Generator - query(参照)

生成されるコード (抜粋)
```
type PostPageQuery = { // レスポンス型
  __typename: 'Post';
  id: string;
  title: string | null | undefined;
};
const usePostPageQuery = useQuery(...); // hooks
```

---

### GraphQL Code Generator - query(参照)

hooksの使い方
```
const Component = () => {
  const { data, loading, error } = usePostPageQuery();
  if (error) return throw error; // エラー
  if (loading) return <div>Loading</div>; // ローディング中
  return <div>{`${data.post.id}: ${data.post?.title}`}</div>;
};
```
---

### GraphQL Code Generator - mutation(更新)

Schema
```
type Mutation {
  update_post(input: UpdatePostInput!): UpdatePostMutation!
}
type UpdatePostInput { id: String! title: String! } // パラメータ
type UpdatePostMutation { post: Post } // レスポンス
```

---

### GraphQL Code Generator - mutation(更新)

オペレーションコード
```
mutation PostPageUpdate($input: UpdatePostInput!) {
  update_post(input: $input) {
    id // ここからレスポンス
    title
  }
}
```

---

### GraphQL Code Generator - mutation(更新)

生成されるコード (抜粋)
```
type PostPageUpdateMutation = { // レスポンス型
  __typename: 'PostPageUpdateMutation';
  post: Post;
};
const usePostPageUpdateMutatiion = useMutation(...); // hooks
```

---

### GraphQL Code Generator - mutation(更新)

hooksの使い方
```
const Component = () => {
  const [mutate] = usePostPageUpdateMutatiion(); // 関数取得
  const handleUpdate = async () => {
    const { error } = await mutate();
    if (error) return throw error; // エラー
  };
  return <button onClick={handleUpdate}></button>;
};
```

---

## 開発の流れ

---

## 開発の流れ

GraphQLでの開発方法は主に2種類
・Code First
型・リゾルバーからスキーマ生成 (我々はこちら)
・Schema First
スキーマからコード生成 (gqlgenなど)

---

## 開発の流れ

[BE] テーブル設計
[ALL] ER図とデザイン見てスキーマ相談
[BE] リソースの型とリゾルバー(ロジック)書く
[BE] スキーマ自動生成
[FE] オペレーション書く
[FE] レスポンス型・hooks自動生成

---

## 開発の流れ - 課題

このチームはBE/FEが別れており、コードファーストのためスキーマを相談するタイミングが見失われがちです。
デザイン・ER図ができたタイミングで、スキーマを相談するタイミングがあるとスムーズに実装を進められます<(__*)> :pray: 

---

## 最後に

kibelaにドキュメントを作成しています 💁
よく使う用語やライブラリのAPIを記載していますので、困ったときにご覧ください。

・GraphQLの用語集
・[FE] ApolloClientの使い方