GraphQL を RESTful API と比較しながら実装して理解する
この記事は GraphQL Advent Calendar 2018、JX通信社 Advent Calendar 2018の19日目です。
NewsDigest で API のバックエンドとして GraphQL を本番利用して1年ぐらい経ちました。GraphQL 自体は、「クエリ言語」という位置づけですが、実際には「API のスキーマ」という使われ方(a.k.a. Anti-REST な何か)が多いと思います。
実際、NewsDigest の API としては、もともと RESTful なものを運用してたのですが、GraphQL へ移行しました。つまり、
- RESTful の代わりとしての利用
- プライベートな API
- アプリから使う API
- サーバーサイドエンジニアがメンテするもの(BFFな文脈で、フロント側のエンジニアがメンテするケースも多いと思いますが)
といった使い方です。GitHub のようなパブリックな API でもなければ、Facebook のようにリソースが複雑に絡まりあっているようなものでもなく、ただシンプルに「アプリ用の、RESTful API の代わり」として使っている感じです。
さて、GraphQL が流行ってくるにつれて「GraphQL は何をやってくれるものなの?」「どう実装したら良いの?」という話が聞かれたりしたので、「REST と比較して実装みたらわかりやすいんじゃないか」という趣旨の記事です。
RESTful vs GraphQL
まず、RESTful な API では、次のような URL が生えてきます。
GET /books
GET /books/1
POST /books
PUT /books/1
DELETE /books/1
一方で GraphQL な API では、最低限必要なのは次の URL だけです。 *1
POST /graphql
そのかわりに、POST のペイロードとして次のようなものを送ります。REST における GET /books
は、
といった具合です。 *2
RESTful な API
今回は簡略化して、 GET /books
と POST /books
だけを扱います。
例として、Node の express で実装してみます。
実装としては単純で、GET /books
と POST /books
というエンドポイントに、それぞれ「本の一覧を取得」と「本を追加」という処理を書いたハンドラー(コールバック)を追加しているだけです。
これは express 固有の話じゃなくて、他の API 用フレームワーク等(例えば Flask や Go の net/http) でも「パスを定義して、それを処理するハンドラーを書く」というのはだいたい同じでしょう。Rails のような MVC な設計であれば、Controller の Action を生やしていくイメージです*3。
GraphQL な API
次に、 apollo というライブラリを使って、GraphQL な API を実装してみます。まずは、 GET /books
に該当するものから実装してみましょう。
先程の REST な API と比較して新しく出てきた概念は、 typeDefs
と resolvers
を定義しているところです。前者は、GraphQL のスキーマを定義していて、後者は、スキーマ上に定義されたリソースをどのように解決するか(要はDBから取ってきてオブジェクトを返すもの)を定義したリゾルバーです。この「スキーマ定義」と「リゾルバー」に出てくる Query
や books
といった変数が、ちょうど、対応しています。
これはまさに、「URL(パス)の定義」と「ハンドラー」の対応関係と一緒です。RESTful な API においては、URL とリソースは対応しているわけですから、REST と GraphQL の違いは、 「リソースをどのような URL で定義するか」と「リソースをどのようなスキーマで定義するか」という違い になります。
今度は、POST /books
も追加してみましょう。GraphQL での Create/Update/Delete の操作は、各操作を関数っぽく定義していきます。*4
Mutation(Create/Update/Delete)に関しても、Query と同じように、フィールドとリゾルバーを定義します。
今回は apollo というライブラリの例ではありますが、Python の graphene や Go の gqlgen などでも全く同じで、「スキーマにフィールドを定義して、それに対応したリゾルバーを用意する」という形になります(graphene はスキーマを class で定義する、という違いはあります)。
GraphQL が提供しているもの
GraphQL 自体は冒頭にも述べたように、ただの「クエリ言語」です。なので、その 実装や、どういうデータベースをバックエンドに持つか、あるいは、HTTPで提供するかどうかさえも関心外 です。
BFF の文脈では、GraphQL な API が提供するのは「秩序ある神エンドポイント /graphql
」です。「1リクエストで、必要なリソースだけをすべて取得する」という手段が提供することによって、無秩序にフロントエンド向け URL を生やすということがなくなるわけです。
また、Prisma のようなものもあります。こういうのは、「レールに乗っけると便利になる、フルスタックフレームワーク」みたいな感じで認識しておくと良いと思います(使ったことないけど)。すでにあるデータベースなどの資産を活かして移行したいような場合とかは、apollo のような薄いものを使うと良いと思います。
GraphQL を使うべきか?
GraphQL は、秩序を守りながら API を提供する手段に過ぎないです。 なので絶対に GraphQL を使うべき理由も、絶対に GraphQL を使うべきでない理由も、あんまりない ような気がしています。
けど、便利なケース/便利じゃないケースはあります。便利じゃないケースとしては、ファイルアップロード(multipart form)とかは一工夫必要です。ちなみに、 NewsDigestでの「REST API の代わりとして使う」は普通に便利なケース でした。
あとは、Prisma や AppSync*5 、apollo-link-state *6 のような、GraphQL のルールに則ることで便利に使えるものも多いので、活用すると DX が上がると思います*7。
メリットに関する補足 ↓
まとめ
まとめると、 RESTful な API の「URL を定義して、対応するハンドラーを実装する」はGraphQL における「スキーマを定義して、対応するリゾルバーを実装する」に該当する ということです。
また、JX通信社では、一緒に GraphQL API を作りたい仲間を募集中です。ちなみに GraphQL API は Go 使ってます。
*1:実際には GET でも取得できるケースも多いと思います。また、どういう URL かは、GraphQL の責務ではありません。例えば、 PUT /hogehoge というエンドポイントだけを生やして、GraphQL API を作ることもできます
*2:イメージをつきやすくするために、かなり簡略化した例です
*3:Rails 久しく触ってないので、誤りがあればすみません
*4:こちらの例では Book 型で返してますが、成功したか失敗したかだけを Bool で返しても特に問題ないです
*5:マネージドな GraphQL APIサーバーです
*6:Redux のような状態管理のライブラリで、フロントエンドの状態管理を GraphQL のスキーマに乗っけることができます
*7:NewsDigestではいずれも使ってないのですが・・・笑
0 件のコメント:
コメントを投稿