ぐるなびのREST APIをHasuraでGraphQLにしてみた
最近いろいろいじくっているGraphQLエンジンのHasuraを使うとPostgresのデータベースに格納したデータに対してGraphQLのクエリが発行できるようになります。
なのでRESTからデータを引っ張ってきてPostgres内に保存すればGraphQLでRESTからGraphQLへの変更が行える事に気づき、ぐるなびのAPIのデータを使って行ってみました。
手順は以下の通り:
- Hasuraの設定を行う
- データ格納するテーブルを設定する
- テーブル間のrelationshipを設定する
- ぐるなびAPIからデータを取ってきてPosgres内に格納する
- GraphQLのクエリを発行してみる
基本これだけ。NodeJS内でGraphQL使ってデータの格納を行いますが、その他の追加のコーディングは必要ありません。
では実際にやってみましょう。
- Hasuraの設定を行う
HasuraはHerokuかDocker経由でインストール可能ですが、今回はDockerを使ってみます。
dockerとdocker-composeをまず自分のマシン内にインストールしてください。
このページに説明があるように、まずdocker-compose.yamlを空のディレクトリ内にダウンロードします。
$wget https://raw.githubusercontent.com/hasura/graphql-engine-install-manifests/master/docker-compose/docker-compose.yaml
その後dockerを起動します。
$ docker-compose up -d
これで問題なければ以下のURLからHasuraが立ち上がります。
http://localhost:8080/console

これでHasuraが使えるようになりました。
2. データ格納するテーブルを設定する
ぐるなびのレストランの基本情報を格納するrestaurantテーブルとレストラン情報の多言語の詳細を格納するrestaurant_descriptionテーブルを用意します。
Hasuraではテーブルの追加にはwebのコンソールから行います。まずページトップの”DATA"をクリックしてAdd Tableをクリックして、テーブル追加ページにいきます。
今回はレストランを格納するrestaurantテーブルを以下の内容になるように追加していきます。
idはprimary keyとして自動で増える様にInteger (auto-increment)を指定しましょう。
Nullableをチェックして必須項目でないところを指定します。またgurunavi_idのUniqueをチェックしておいて、foreign keyを後で指定できるように

“Create”をクリックして、テーブル作成は完了です。
次にrestaurantとjoinするrestaurant_descriptionテーブルを用意します。ぐるなびには多言語でレストランの詳細を返すAPIがあるのでその情報をこのテーブルに格納します。
langというカラム内に言語コードを保存します。ぐるなびAPIでは以下の言語コードが扱えます。その他にもbusiness_hourなど違う言語でお店の情報を保存します。
ja: 日本語, zh_cn: 中国語 (簡体字), zh_tw: 中国語 (繁体字), ko: 韓国語, en: 英語

Primary Keyはidを選択して、”Create”をクリックして保存は完了です。
3. テーブル間のrelationshipを設定する
次にこのテーブル間のrelationshipを定義します。relationshipを定義する事でテーブル間を結合する以下のようなGraphQLのクエリが発行できるようになります。
query restaurant {
restaurant {
id
name
restaurant_descriptions {
id
lang
pr_short
}
}
}
Hasuraでは1:nの関係をArray relationshipと呼び、1:1の関係をObject relationshipと呼んでいます。
restaurantとrestaurant_descriptionは1:nの関係なのでArray relationshipを設定します。
まず最初にrestaurant_descriptionテーブルからrestaurantテーブルへ対してのforeign keyを設定します。
restaurant_descriptionのModifyタブからgurunavi_idのforeign keyの設定を行います。

これをSaveして設定を完了してください。
そしてrestaurantテーブルからRelationshipsタブに行くと以下の様な画面が出てきてArray relationshipを設定しない?と出てくるのでAddをクリックして、先に進めましょう。

このrelationshipの名前はrestaurant_descriptionsとでもしておきましょうか。保存後以下の画面になります。

“+ Add a manual relationship”はforeign keyの設定がない状態でテーブル間のrelationshipを作るのに使います。
これでrelationshipの設定は完了です。
4. ぐるなびAPIからデータを取ってきてPosgres内に格納する
今回はNodeJSのスクリプトを使ってデータをローカルのpostgresに保存します。
こちらが今回のコードのgithubです。
まずこのコードをcloneします。
$git clone git@github.com:moksahero/hasura-gurunavi-graphql.git
レストラン検索APIでレストランの基本情報を取得し、restaurantテーブルに保存します。import_restaurants.jsにコードがあります。
その後多言語版レストラン検索APIで日本語や外国語のレストランの説明分をrestaurant_descriptionテーブルに保存します。import_restaurant_descriptions.jsにコードがあります。
NodeJSのコードを直接見てもらえればいいので、記事内では詳細な説明は行いませんが、mutationを使ってpostgresに対してデータを追加しています。
各ファイルの中の[replace with keyid]をぐるなびWebサービスから提供されるkeyidで置き換えてください。
置き換えた後はnpmインストールを行い必要なモジュールのgraphqurlとaxoisをインストールします。
$npm install
graphqurlはHasuraチームが作成したGraphQLのクエリをシンプルに呼べるようにしたライブラリです。Apolloの上に実装されています。
そしてnode経由でスクリプトを動かします。自分のマシンのNodeのバージョンはv9.11.1です。
$node import_restaurants.js
このコードでは銀座近辺のレストラン10件をareacode_l: ‘AREAL2101’から10件ほど保存しています。
これでエラーが出ずに終了すればrestaurantテーブルへのインポートは終了です。
エラーがある場合はコード内の以下からconsole.log()をしているのでエラーの詳細が表示されます。
await client.query(createRestaurantDescriptionMutation, restaurantDescriptionDetails).catch(error => {
console.log(JSON.stringify(error, null, 2))
})
同様に以下のコマンドを走らせ、言語毎のレストランの情報をrestaurant_descriptionテーブルにインポートします。
$node import_restaurant_descriptions.js
こちらもエラーが出なかったらインポート完了です。
5. GraphQLのクエリを発行してみる
データが格納できたので、実際クエリを発行してみます。
http://localhost:8080/console
このURLからHasuraのconsoleに入り、以下のクエリを発行してみましょう。
query restaurant {
restaurant {
id
name
name_kana
gurunavi_id
}
}
きちんとrestaurantテーブル内の指定したカラムだけが返ってきていますね。

今度はrestaurantとrestaurant_descriptionをjoinしたクエリを発行してみましょう。
query restaurant {
restaurant {
id
name
restaurant_descriptions {
id
lang
pr_short
}
}
}
Joinされた結果が返ってくるのが確認できますね。

Hasuraはクエリの中にwhere文を追加して結果をフィルタリングできます。langがenかjaのrestaurant_descriptionだけのクエリを発行してみましょう。
query restaurant {
restaurant {
id
name
restaurant_descriptions (
where: {lang: {_in: ["en", "ja"]}}
)
{
id
lang
pr_short
}
}
}
英語と日本語だけの詳細情報が返ってきましたね。

ここではwhere: {lang: {_in: [“en”, “ja”]}}を指定して結果をフィルタリングしています。Hasuraはその他のフィルターはHasuraの以下のページに詳細があります。
Postgresにデータを突っ込むだけでSQLライクなGraphQLのクエリが発行できるようになるHasura、いろんな可能性を感じています。