Hasura で PostgreSQL に GraphQL で問い合わせ、JSON を受け取ってみた

[feedly](https://www.feedly.com/) に広告が出ていたので [Hasura](https://hasura.io/) というものを使ってみました。PostgreSQL のデータを JSON で返す [GraphQL](https://graphql.org/) API を簡単に作れます。Hasura について詳しくは [GraphQLとRDBMSのいいとこ取りをしたHasura](https://medium.com/@takeshiamano/graphql%E3%81%A8rdbms%E3%81%AE%E3%81%84%E3%81%84%E3%81%A8%E3%81%93%E5%8F%96%E3%82%8A%E3%82%92%E3%81%97%E3%81%9Fhasura-5c8a5ab4cca) などをご覧ください。

GraphQL を初めて使ったくらいなので、どこまで正確に書けるかわかりませんが、チュートリアルの進め方のコツを掴めたように感じたため、今後使う方の参考になるかもしれないと思い、やったことを書き残すことにしました。

## 導入と最初の問い合わせ

試してみるには https://hasura.io/ で推奨されている通り、Heroku が良いです。外出先でタブレットとかから操作できます。[Quickstart with Heroku](https://docs.hasura.io/1.0/graphql/manual/getting-started/heroku-simple.html) を参考に、Heroku にデプロイします。

デプロイできたら、[Making your first GraphQL query](https://docs.hasura.io/1.0/graphql/manual/getting-started/first-graphql-query.html) の通りにやれば JSON が返ってきます。

## テーブルを JOIN した結果を取得する

もう少し現実的な例として、JOIN した結果を受け取りたい場合にどうすれば良いか、少しわかりにくかったです。[Schema](https://docs.hasura.io/1.0/graphql/manual/schema/index.html) には「GraphQL スキーマは書かなくていい」と書いてあり、ここで「じゃあこの先読まなくていいのかな？」と誤解してハマってしまったのですが、実際には [Basics](https://docs.hasura.io/1.0/graphql/manual/schema/basics.html) に読み進まないと、実用的な問い合わせを書けません。

### article テーブル、author テーブルを作り、mutation や query を試す

article テーブルを作ります。Data タブで [Basics](https://docs.hasura.io/1.0/graphql/manual/schema/basics.html) の画面の通りにテーブルを作ります。

article テーブルのスキーマの確認をします。GraphQL タブに戻って、"< Docs" をクリックして Documentation Explorer を開き、query_root をクリックして開くと、 [Basics](https://docs.hasura.io/1.0/graphql/manual/schema/basics.html) のページに記載の Query と同じものが出ていることがわかります。また、"< Schema" をクリックして画面を戻り、mutation_root をクリックして開くと、やはり [Basics](https://docs.hasura.io/1.0/graphql/manual/schema/basics.html) のページに記載の insert/upsert mutation や update mutation、delete mutation と同じものが登録されていることを確認できます。ただし、フィールドの並び順はなぜか異なります。Object type の確認方法はわかりませんでした。

同様に author テーブルも作成します。id を Integer (auto-increment)、name を Text にし、Primary key は id を指定すると良いと思います。

これで、[Basics](https://docs.hasura.io/1.0/graphql/manual/schema/basics.html) のページ末尾の query や mutation が動きます。

### Relationships を作る

[Relationships between tables/views](https://docs.hasura.io/1.0/graphql/manual/schema/relationships.html) の Using Foreign Keys の通りに外部キー制約を追加し、relationships を設定します。この時点で query を実行しても insert していないので結果は空っぽです。

文書と同じような問い合わせ結果を得るために次の mutations を実行し、テーブルに行を挿入します。

```GraphQL
mutation add_author {
  insert_author(
    objects: [
      {id: 1, name: "Justin"},
      {id: 2, name: "Beltran"},
      {id: 3, name: "Sidney"},
      {id: 4, name: "Anjela"}
    ]
  ) {
    affected_rows
  }
}

mutation add_article {
  insert_article(
    objects: [
      {id: 1, title: "sit amet", content: "", rating: 5, author_id: 4},
      {id: 2, title: "a nibh", content: "", rating: 1, author_id: 2},
      {id: 3, title: "amet justo morbi", content: "", rating: 5, author_id: 4},
      {id: 6, title: "sapien ut", content: "", rating: 2, author_id: 3},
      {id: 9, title: "sit amet", content: "", rating: 5, author_id: 2},
      {id: 11, title: "turpis eget", content: "", rating: 3, author_id: 3},
      {id: 14, title: "congue etiam justo", content: "", rating: 3, author_id: 3},
      {id: 15, title: "vel dapibus at", content: "", rating: 1, author_id: 1},
      {id: 16, title: "sem duis aliquam", content: "", rating: 4, author_id: 1}
    ]
  ) {
    affected_rows
  }
}
```

その後 Document に記載の query を実行すると、似たような結果が得られると思います。例によってフィールドの並び順は異なります。

次に、author_avg_rating ビューを作ります。Data タブの左下の SQL を選択し、Track table のチェックが入っていることを確認して、次の問い合わせを実行します。ビューが作られ Hasura に追加されます。

```sql
CREATE VIEW author_avg_rating (id, avg) AS
SELECT author_id, avg(rating)
FROM article
GROUP BY author_id;
```

後は [Relationships between tables/views](https://docs.hasura.io/1.0/graphql/manual/schema/relationships.html) の Without Foreign Keys の通りに作業を進めていきます。スクリーン ショットと説明文が一致していない箇所は説明文の通りに入力してください。

Query の例はなぜか初期状態で空白に見えますが、クリックすると表示されます。例を実行するとだいたい同じような結果を得られます。

## そしてその先へ

[Customise schema with views](https://docs.hasura.io/1.0/graphql/manual/schema/views.html) 以降はやっていませんが、同じノリで何とかなるのではないでしょうか。後は任せた……。ぐふっ…！

## Where を使って結果を絞り込む

チュートリアルから離れて、where を使う例です。[GraphQL 公式ページの例](http://graphql.github.io/learn/execution/#producing-the-result) を見ると、問い合わせには以下のような形式が使われることが多いのだろうと思われますが、これは Hasura では動きません。

```GraphQL
query {
  author(id: 1) {
    name
  }
}
```

GRAPHIQL タブで "< Docs" をクリックして Documentation Explorer を開き、query_root をクリックして開くと、以下のように書いてあると思います。

```
author(
limit: Int
offset: Int
order_by: [author_order_by!]
where: author_bool_exp
): [author!]!
```

author_bool_exp をクリックすると順々に構文規則が表示されるため where に記述する形式がわかる……はずなのですが、GraphQL に不慣れなためよくわかりませんでした。[Filter query results / search queries](https://docs.hasura.io/1.0/graphql/manual/queries/query-filters.html) あった例を見て、ようやくわかりました。id が 1 であるレコードの name を抽出する問い合わせは以下の通りです。

```GraphQL
query {
  author (where: {id: {_eq: 1}}) {
    name
  }
}
```

## 小並感

外部キーとかを使ってきれいにテーブルを作ると Hasura が relationships を suggest してくれるから、簡単に GraphQL の API を作れることがわかりました。外部キーなんて役に立たないという意見も聞きますが、今日はありがたみを感じることができました。

DB を作ってる側には便利なツールだけど、データを読むツールを作る人にとって GraphQL って便利なのかな？、と疑問に思いました。

個人的に PostgreSQL で再帰問い合わせをすることが多いんですが、[GraphQL では循環してしまうとだめっぽい](https://github.com/facebook/graphql/issues/91#issuecomment-254895093)のは残念でした。View を作っておくなどする必要があるのかなと思いました。

Hasura はインドの会社のようで、[阿修羅＝鬼＝daemon と Haskell で Hasura](https://hasura.io/about) だそうです。[GREE からも出資されています](https://www.greeventures.com/portfolio/hasura)が日本語のツイートがほとんどなかったので、どの界隈で注目されているのか気になります。これでも Haskell プログラマーの端くれなんだけど、初耳だったよ!?

本格的に使うなら [Medium の Hasura タグ](https://medium.com/tag/hasura/latest)の記事が参考になりそうです。