React+GraphQLをHeroku+Hasura でセットアップする

![logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/ea2c16b9-1671-6f5e-c7f7-f909d3529c51.png)

今回は以前から気になっていた GraphQL を Heroku + Hasura で構築し、React から Apollo Client で呼び出すまでの流れを備忘録として記事にしたいと思います。
実際に試してみると、Firebase と同じくらいに簡単にサーバーを構築することができました。

# Hasura でプロジェクトを作成する

Hasura で新しく GraphQL のサーバーを作成します。

<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/21a6d507-05cb-859a-2bd3-4eb99edae2ea.png">

PostgresSQL サーバーを選択しますが、Heroku を使用する場合はアカウントリンクをすることで Heroku 側のプロジェクトを自動的に初期化してくれます。

<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/bfd22126-8c06-821e-6027-65fba55597f5.png">

プロジェクトの作成が完了すると管理画面が表示され、GraphQL のエンドポイントやおなじみの GraphQL の画面を確認することができます。

<img width="800" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/0f12f469-d7af-26bc-6469-95cd5c1681e2.png">

# サンプルのデータを追加する

この状態ではデータベースに何もデータがない状態なので、ダミーデータを入れてみます。
データは何でも良いですが、[このリポジトリ](https://github.com/datacharmer/test_db) の [load_employees.dump](https://github.com/datacharmer/test_db/blob/master/load_employees.dump) から一部データを取ってきました。

Create Table からテーブルの型を決めます。今回は次のようにしました。

<img width="800" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/2c96794d-0ad5-52b8-6867-b6d7637d5fb2.png">

テーブルの型を決めたら Insert Row からデータをいくつか入れます。

<img width="800" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/61f12835-3ef5-aa3c-bd7e-300b806d9408.png">

最終的には次のようになりました。

<img width="800" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/4a81eed4-d061-742a-325b-a47d4d0fbc03.png">

データの入力が完了すると、トップページから GraphQL のクエリを試すことができます。
クエリ構文を覚えていなくても Explorer から選ぶだけで自動でクエリが生成されるのでかなり簡単ではないでしょうか。

<img width="1000" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/82be9d6f-65de-0f26-8cfe-8e164b43e592.png">

# React アプリケーションの作成

上記で用意した GraphQL サーバーを React+Apollo Client で呼び出してみます。
GitHub も公開しているので良かったらクローンして試してみてください。

<a href="https://github.com/TakaoNarikawa/React-Apollo-GraphQL"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/TakaoNarikawa/React-Apollo-GraphQL.png" width="460px"></a>

## Client を作成し Provider に与える

React hooks から GraphQL を呼び出せるように、作成した `client` を Provider で 指定します。

```tsx:index.tsx
import { ApolloProvider } from "@apollo/client";
import { ApolloClient, InMemoryCache } from "@apollo/client";

const client = new ApolloClient({
  uri: process.env.REACT_APP_SERVER_URL,
  cache: new InMemoryCache(),
});
ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById("root")
);
```

コンポーネント側から `useQuery` を使用してクエリを実行します。

```tsx:App.tsx
import { useQuery, gql } from "@apollo/client";

const GET_EPLOYEES = gql`
  query GetEmployees {
    employees {
      birth_date
      emp_no
      first_name
      hire_date
      last_name
    }
  }
`;

function App() {
  const { data } = useQuery(GET_EPLOYEES);
  return (
    <Container>
      <Pre>{JSON.stringify(data, null, 2)}</Pre>
    </Container>
  );
}
```

これを表示してみると、次のようになります。
先程 Hasura で登録した内容を表示できていることが分かります。

<img width="800" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/d31895c4-8f39-95ff-a588-4531ed953fb6.png">


## 取得するデータを型安全にする

ここからは TypeScript を使用する人向けです。
先程取得した `data` は `any` 型となっています。これを [apollo-tooling](https://github.com/apollographql/apollo-tooling) を使用することでクエリ文から型を生成できるようになります。

<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/d99ec14a-7d05-9368-37e2-d57f15843477.png">

まず Apollo Tooling をインストールします。

```sh
yarn global add apollo
```

そして React プロジェクトのルートに `apollo.config.js` を作成します。
GraphQL の URL もこの中に指定します。

```js
module.exports = {
  client: {
    name: "client",
    includes: ["src/**/*.ts", "src/**/*.tsx"],
    tagName: "gql",
    addTypename: true,
    service: {
      // remote endpoint
      name: "sever",
      url: "https://xxxxxxxxxxxxxxxxx/graphql",
    },
  },
};
```

そして `client:codegen` コマンドで型定義ファイルを生成します。

```sh
apollo client:codegen --target typescript types
```

完了すると、 `src/types/xxxx.ts` に生成されたファイルが保存されます。

```ts
export interface GetEmployees_employees {
  __typename: "employees";
  birth_date: any;
  emp_no: number;
  first_name: string;
  hire_date: any;
  last_name: string;
}

export interface GetEmployees {
  /**
   * fetch data from the table: "employees"
   */
  employees: GetEmployees_employees[];
}
```

`useQuery` のジェネリクスにこの生成された型を指定すると、`data` に型が付きます。 🎉

```tsx
const { data } = useQuery<GetEmployees>(GET_EPLOYEES);
```

<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588399/4f8bb57d-b309-6533-8109-55b3bb212150.png">

# おわりに

DB サーバーの用意から React での使用まで、想像より遥かに簡単に行うことができました。
さらには TypeScript の型付けも自動で行うことができ、かなり DX が向上すると思います。
また、Hasura では データベース内のアクセス権限などの管理はできるようですが、認証などは搭載していないようです。このあたりは Firebase と組み合わせて使用するのが一番簡単かもしれないですね。

# 追記

JOIN などを行う方法を記事にしました。(https://qiita.com/TakaoNarikawa/items/c599a84c8f587fdcc867)

# 参考

- https://dev.classmethod.jp/articles/apollo-good-dx