Edited at

Railsで育った僕がGraphQLに感動した話

More than 1 year has passed since last update.


僕とRailsとREST

初めてのWeb言語が Ruby on Railsで

Railsから多くのものを学んだ自分にとっては

Railsは 最強のフレームワーク

RailsはREST,CRUDをベースにしており

それこそが最先端!

一番イケていると思い込んでいました。

なのでRailsGraphql書くなんて、、、、

そんな、、、Railsでも最も

大きなRailRESTから外れてしまうよ、、、

ありえない、、、って思っていたのですが、

いざ使ってみるとGraphql最高!と感じたので

それについて書きます。


RESTなAPI

レシピサイトを作るとします。

エンドポイント(基本GET)が下記の表になるとすると

endpoint
返す値

/recipes
レシピのURL一覧

/recipes/:id
idで指定されたレシピ一つ

/recipes/:id/foods
idで指定されたレシピで使われる食材たち

/foods
食材URLたち

/foods/:id
idで指定された食材

/foods/:id/recipes
idで指定された食材が使われているレシピを表示

こんなかんじ。


レシピの一覧から食材情報ほうを取ろうとする


1.まず recipesでレシピのURL一覧を取得し

localhost/recipes

[

{"url":"recipes/1"},
{"url":"recipes/2"},
{"url":"recipes/3"}
]


2.そのあとに上のrecipeのurlのidをもとにrecipeの詳細を取得します。

localhost/recipes/1

{

"recipe": [
{
"recipe_id": 1,
"user_id": 2,
"title": "卵かけご飯",
"description": "ご飯の上に卵をかけ、そのあと醤油をかけます。",
"foods": [
{
"url": "/foods/1"
},
{
"url": "/foods/2"
},
{
"url": "/foods/3"
}
]
},
{
"recipe_id": 2,
"user_id": 3,
"title": "納豆ご飯",
"description": "ご飯の上納豆を載せます。。",
"foods": [
{
"url": "/foods/2"
},
{
"url": "/foods/4"
}
]
}
]
}


3.上のfoodのurlのidをもとにfoodの詳細を取得します。

localhost/foods/1

{

"food_id":1,
"description":"卵",
"image":"egg.png"
}

URLが

resourceの場合は配列を返し、

resourceの場合は詳細情報を返すので

わかりやすいものの

foodまで情報を取ろうとすると

クライアントで何回もAPIにリクエストを送ることになります。


くずれたRESTなAPI

上のようにきっちり

RESTなAPIを実サービス書くことはないと思います。

僕が書く場合は

GET /recipes(レシピの一覧の食材を表示したい場合)対して

{

"recipe": [
{
"recipe_id": 1,
"user_id": 2,
"title": "卵かけご飯",
"description": "ご飯の上に卵をかけ、そのあと醤油をかけます。",
"foods": [
{
"food_id": 1,
"title": "たまご"
},
{
"food_id": 2,
"title": "ごはん"
},
{
"food_id": 3,
"title": "醤油"
}

]
},
{
"recipe_id": 2,
"user_id": 3,
"title": "納豆ご飯",
"description": "ご飯の上納豆を載せます。",
"foods": [
{
"food_id": 2,
"title": "ごはん"
},
{
"food_id": 4,
"title": "納豆"
}
]
}
]
}

のようなレスポンスを返すように書くと思います。

ですが、listとは別に,画像のグリッドのページが作りたいなど

サービスによって様々な要求があり

URL?grid=trueのようなパラメータをつけて、コントローラーで制御したり、

新しいエンドポイント作り、処理はconcernに逃して、共通化したり

RESTを崩しながら、APIを作って行くと思います。


Graphqlでは

エンドポイントは一つなので

例えばPOST:localhost:3000/api/とします。

queryを一つ定義してしまえば、対応することでができます。

ここではレシピ一覧のqueryのみ書きますが、

すでにfood,recipequeryに定義されているとします。


例えば上のレシピ一覧の場合

graphql-rubyを参考

以下のgraphqのクエリをlocalhost:3000/api/に投げると

上のくずれたREST と同一の情報を取得することができます。

query 

recieps {
edge{
node{
recipe_id
title
  image
description
foods {
edges{
node{
food_id
title
}
}
}
}
}
}

edge,nodeはライブラリの仕様です、、、

ネストする場合必要です。


画像のグリッド表示にしたい場合は

foodにimageを付け加えれば良いので

query 

recieps {
edge{
node{
recipe_id
title
image
description
foods {
edges{
node{
food_id
title
image # ここ
}
}
}
}
}
}


食材をベースにしたレシピの一覧の場合は

レシピの画像と食材の画像を必要の両方必要とするなら

foodrecipeの両方に

imageをつければよいのです。


まとめ


graphqのクエリ

基本一つqueryを書いてしまえば

配列にすることも、ページネーションすることも容易です。

なのでコードもDRYにすることできます。

API側でqueryを実装してしまえば

クライアントが欲しい情報はクライアントで定義でき、

一つのリクエストで必要な情報を取得できます。


ドキュメントとエンドポイント

RESTで開発を進めて行くと

エンドポイントとドキュメントが膨大に増え

開発が苦しくなってきますが

Graphqlは一つのエンドポイントのみであり

定義されるスキーマにドキュメントが含まれているので

ドキュメントを省略することができます。