Hasuraと既存のPostgresを使ってGraphQLを使えるか？

# GraphQLを導入して開発効率を上げたいなぁ

GraphQLと言っても、REST APIと同じくRDBの設計は必要になります。
一方で、GraphQLはエンドポイントが１つなのでREST APIと違ってAPI設計がシンプルになるのかなと思われます。

また、GraphQLの導入にHasuraを使うことで、画面ポチポチでテーブル作成やカラムへのアクセス制御などができ、リリースまでの開発速度が上がることを期待しています。


```mermaid
gantt
    title REST APIとGraphQLの工数比較イメージ
    section REST API
    設計       :a1,  2d
    実装       : 2d
    リリース     :milestone, 0d

    section GraphQL
    設計        :b, 1d
    画面ポチポチ :1d
    リリース     :milestone, 0d
```

# 結論

:::note
- 既存のPostgresデータベースでもHasuraから接続すればすぐに使える
- GraphQLで使うテーブルのみ指定すればスモールスタートできる
:::

---
以下は、検証手順です。

# Hasura の環境構築

公式リファレンスにしたがって、`docker-compose.yml`テンプレートを取得して実行してみます。

```bash
$ curl https://raw.githubusercontent.com/hasura/graphql-engine/stable/install-manifests/docker-compose/docker-compose.yaml -o docker-compose.yml

$ docker-compose up -d
```

`http://localhost:8080/console`に接続するとHasuraコンソール画面を開けます。
しかし、まだDBを認識していません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/d408208a-3b50-1bd5-9ff7-5fc0da36e8ce.png)

# DBスキーマを事前に作成して接続する

公式リファレンスでは、このままHasura経由でDBスキーマを作成します。
しかし、今回は既存のPostgresへの導入を検証したいので、自身でDBスキーマを作成します。

## Postgresデータベースのコンテナに入る

`docker-compose ps`コマンドで実行中のコンテナを確認すると、Hasura（graphql-engine）とpostgresの2つを確認できます。

```bash
$ docker-compose ps
>       Name                      Command              State                    > Ports                  
> -------------------------------------------------------------------------------------------------------
> graphql-engine_1   graphql-engine serve            Up      0.0.0.0:8080->8080/tcp,:::8080->8080/tcp
> postgres_1         docker-entrypoint.sh postgres   Up      5432/tcp
```

そこで、postgresのコンテナに入ってスキーマとテーブルを作成していきます。

```bash:postgresコンテナに入りDB接続する
$ docker-compose exec postgres bash
$ psql postgres://postgres:postgrespassword@postgres:5432/postgres
```

postgresの接続文字列は、ダウンロードしてきた`docker-compose.yml`に記載してあります。

## DBスキーマとテーブルを作成する

```sql:DBスキーマとテーブルを作成
-- 注意： これはpostgresコンテナの中です
CREATE SCHEMA myschema;
CREATE TABLE myschema.users(id serial, name text);

-- 結果を確認
\dt myschema.*

>           List of relations
>   Schema  | Name  | Type  |  Owner   
> ----------+-------+-------+----------
>  myschema | users | table | postgres
> (1 row)
```

```sql:データ登録
INSERT INTO myschema.users(name) values('Taro'), ('Jiro'), ('Saburo');

-- 結果を確認
SELECT *  FROM myschema.users;

>  id |  name  
> ----+--------
>   1 | Taro
>   2 | Jiro
>   3 | Saburo
> (3 rows)
```

## HasuraコンソールからDB接続設定をする

`http://localhost:8080/console/data/manage/connect`に接続して設定します。
postgresの接続文字列は、ダウンロードしてきた`docker-compose.yml`に記載してあります。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/e8b702d2-e2ac-d526-5b13-be7c4ebee093.png)

接続に成功してスキーマを認識していますが、テーブルは認識されていません。
そこで、Trackボタンを押してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/e9ae09e1-321f-66ff-539f-bc770bccfe9f.png)

無事にテーブルがHasuraに認識されました。
**ここで選択したテーブルのみがGraphQLの対象になるので、GraphQLで使うテーブルのみ指定すればスモールスタートできそうです。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/2c64f60f-1e2b-3fae-4be4-dc5d7e2470b6.png)


## 動作確認

### Hasuraコンソールから

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/6e4a24ec-f520-7468-05e1-e2a5545a09ef.png)

### フロントエンド（Svelte）から動作確認

業務でSvelte（SvelteKit）を使っているので、フロントエンドでの使い方を試してみます。
公式リファレンスのサンプルコードをそのまま流用してます。

<details><summary>サンプルコード</summary><div>

```html
<script context="module" lang="ts">
	/** @type {import('@sveltejs/kit').Load} */
	export async function load({ fetch }) {
		const endpoint = 'http://localhost:8080/v1/graphql';
 		// NOTE: 認証は無視してadminで動作確認
		const headers = {
			'content-type': 'application/json',
			'x-hasura-role': 'admin'
		};
		const graphqlQuery = {
			operationName: 'fetchUsers',
			query: `query fetchUsers { myschema_users { id name } }`
		};

		const options = {
			method: 'POST',
			headers: headers,
			body: JSON.stringify(graphqlQuery)
		};

		const response = await fetch(endpoint, options);
		const data = await response.json();

		return {
			props: {
				users: data.data.myschema_users
			}
		};
	}
</script>

<script lang="ts">
	type User = { id: number, name: string };

	export let users: User[];
</script>

<ul>
	{#each users as user}
		<li>{user.name}</li>
	{/each}
</ul>

```

</div></details>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134221/5e2a7839-7186-57f5-a60c-497160168b44.png)

動いてる :tada: 

# 参考

https://hasura.io/docs/latest/graphql/core/getting-started/docker-simple/

https://hasura.io/docs/latest/graphql/core/databases/postgres/schema/using-existing-database/

https://hasura.io/blog/how-to-request-a-graphql-api-with-fetch-or-axios/