Github PagesでGraphQL(AppSync)を使用する

# はじめに
この記事はGithub Pagesで公開したReact × TypeSCriptアプリで、GraphQL(AppSync)を使えるようにする手順をまとめたものです。あくまで手段のまとめなので、知識がどうこう、というのは薄めです。

- GraphQLがなんとなくわかる(スキーマ等)
- react、TypeScriptをなんとなくでも使ったことがある。

といったレベルだとスムーズに読み進められるかな～と思います。

# 目次
[0. 環境](#0-環境)
[1. 事前準備](#1-事前準備)
[2. AppSyncでGraphQLサーバーを作成する。](#2-AppSyncでGraphQLサーバーを作成する。)
[3. Apollo Client & GraphQL](#3-ApolloClient&GraphQL)
&nbsp;&nbsp;&nbsp;&nbsp; 3-1. Apollo Client
&nbsp;&nbsp;&nbsp;&nbsp; 3-2. GraphQL
[4. 環境変数の利用](#4-環境変数の利用)
&nbsp;&nbsp;&nbsp;&nbsp; 4-1. Github Secrets
&nbsp;&nbsp;&nbsp;&nbsp; 4-2. ワークフローファイルの作成(yaml)

## 0. 環境
##### Node
```shell
% node -v   
v16.15.0
```
##### npm
```shell
% npm -v
8.5.5
```

## 1. 事前準備

[Github](https://github.com/)にてpublicでリポジトリを作成しておいてください。

ローカルで新規reactプロジェクトを作成します。今回はTypeScriptで開発するので、--template typescriptオブションをつけます。
```shell
% npx create-react-app "アプリ名" --template typescript
```
今作ったプロジェクトに移動し、gitのremoteの設定を行います。
```shell
% cd アプリ名

% git init
% git remote add origin "GithubのSSH"
```
なお、remoteが追加できているかどうかは、
```shell
% git remote -v

origin  git@github.com:Githubの名前/リポジトリ名.git (fetch)
origin  git@github.com:Githubの名前/リポジトリ名.git (push) 
```
となっていれば成功です。
## 2. AppSyncでGraphQLサーバーを作成する。
これに関しては分かりやすい記事がすでに投稿されているのでそちらを参照していただけると幸いです。

https://qiita.com/umamichi/items/43e8ca7c86e937e4bf54

こちら、今回は認証モードをAPIキーで構築しています。設定から変更、確認変更できます。また、
- API KEY
- API URL
の二つは後で使用するので、メモしておくとスムーズです。
**機密情報なのでGithubにこれらの値を直接公開することの無いように注意しましょう。**

## 3. Apollo Client & GraphQL
### 3-1. ApolloClient
次に、src/index.tsxにApollo Clientを使用するためのコードを書いていきます。
まず、@apollo/clientをインストールします。
```shell
% npm install @apollo/client graphql --save
```
次にsrc/index.tsxを以下のように書き換えます。
```typescript

import ReactDOM from 'react-dom/client';
import './index.css';
import App from './Pages/App';
import { ApolloClient, InMemoryCache, ApolloProvider, HttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

//GraphQL APIのエンドポイント用の変数
const endPoint = process.env.REACT_APP_GRAPHQL_ENDPOINT;

//APIキー認証用の変数
const apiKey = process.env.REACT_APP_API_KEY;
 
const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      'x-api-key': apiKey,
    }
  };
});
// Apollo Clientのインスタンスを作成
const client = new ApolloClient({
  cache: new InMemoryCache(),
  link: authLink.concat(
    new HttpLink({
      uri: endPoint,
    })
  )
});

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

root.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>
```
Apollo Clientを初期化し、ApolloProviderでラップします。

### 3-2. GraphQL
ついでにGraphQLも書いてしまいましょう。

src/Appに以下を追記します。
```typescript
import React, { useEffect } from 'react';
import logo from './logo.svg';
import './App.css';
import { gql, useQuery } from '@apollo/client';

function App() {
  //バッククォートです
  const GET_DATA = gql`
  query ListEvents {
    listEvents {
      items {
        id
        name
        where
        description
      }
    }
  }
  `;

  const { data, loading } = useQuery(GET_DATA);
  useEffect(() => {
    if (data) {
      console.log(data);
    }
  }, [loading]);
  
  return (
    //省略
  )
```
今回はAppSyncのイベントアプリを元にしているため、スキーマは上記の通りです。別環境でやっている場合はスキーマを都度変えてください。

Apollo Client、GraphQLを包括的にまとめていらっしゃる記事がありましたので、ぜひご覧ください。

https://qiita.com/seya/items/26c8a0dc549a10efcdf8

## 4. 環境変数の利用
### 4-1. Github Secrets
一旦ソースコードから離れて、Githubに戻ります。

リポジトリの Setting -> Secrets and variables -> Actionから、環境変数の設定ページに飛びます。

右上の new repository secret から、環境変数を追加します。
- REACT_APP_API_KEY -> API KEY
- REACT_APP_GRAPHQL_ENDPOINT -> API URL
- MY_TOKEN -> Githubのトークン *1
を書き込みます。

*1: Githubのトークンは、**ユーザー**のsettings ->  Developer settings -> Personal access tokens -> Tokensのgenerate new tokenで生成できます。

**※注意 REACT_APP_○○ という名前で無いとReactが環境変数を認識してくれません。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/0b195ccb-0a71-9767-a680-a259c3f1b7b7.png)

### 4-2. ワークフローファイルの作成(yaml)
次に設定した環境変数をReactで読み込めるようにしていきます。

この為、Github Actionを利用し、build & deploy時に環境変数を取得できるようにする為のワークフローファイルを作成します。

ルートディレクトリに.github/workflows/main.ymlを作成します。
```shell
% mkdir -p .github/workflows
% cd .github/workflows
% touch main.yml
```
作成したyamlファイルに以下を記述します。
```yaml
name: Build and Deploy

on:
  push:
    branches:
      - master

env:
  REACT_APP_API_KEY: ${{ secrets.REACT_APP_API_KEY }}
  REACT_APP_GRAPHQL_ENDPOINT: ${{ secrets.REACT_APP_GRAPHQL_ENDPOINT }}

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - name: Build
      run: |
        npm ci
        npm run build

    - name: Deploy
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.MY_TOKEN }}
        publish_dir: './build'
```
この時、ルートディレクトリにbuildディレクトリを作成しておきます。
デフォルトでは./buildがgitIignoreに設定されているので、コメントアウトしておきます。


yamlファイルではありませんがpackage.jsonに
```json
"homepage": "https://ユーザー名.github.io/リポジトリ名/"
```
を追加しておきましょう。

ここまで出来たらpushします。
(gitignoreの./buildは戻しておきます。)

## 終わりに
ここまでの流れで、gh-pageブランチが自動生成され、github pageが更新されているかと思います。正しくクエリ出来ていれば、開発者ツールのコンソール欄にデータが表示されているはずなので確認してみてください。

素人故に誤りがあるかもしれませんが、指摘いただけると幸いです。

## Q & A
**Q:** github pageが真っ白です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396501/4de6ad14-e3af-63f4-8405-b1486956593b.png)
**<font color="red">A :</span>** package.jsonにhomepageプロパティは追加しましたか?　また、ルートディレクトリにbuildディレクトリは追加しましたか?
___
**Q :** pushしたら次のエラーが出てbuildが失敗したんだけど...
```shell
src/App.tsx
  Line 25:6:  React Hook useEffect has a missing dependency: 'data'. Either include it or remove the dependency array  react-hooks/exhaustive-deps
```

**<font color="red">A :</span>** このエラーは"useEffect"に渡された依存関係配列に、必要なデータである"data"が含まれていないことによります。が、これだとデータが描画されるたびに呼ばれてしまうので、今回は↓
```typescript
  useEffect(() => {
    if (data) {
      console.log(data);
    }
    // 以下のコメントによってEsLintのエラーを無視します、
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [loading]);
```
で対応しました。(別プロジェクトではこんなエラー出なかったんだけどなぁ...)

# 参考
- [AppSync & GraphQL 入門](https://qiita.com/umamichi/items/43e8ca7c86e937e4bf54)
- [世の中のフロントエンジニアにApollo Clientを布教したい](https://qiita.com/seya/items/26c8a0dc549a10efcdf8)