API利用者目線でのGraphQLとRESTの違い
これまでREST/SOAP APIしか利用したことがなかったのですが、最近初めてGraphQLで作られたAPIをクライアント側として利用する機会がありました。この記事では初めてGraphQL APIを使う際、事前に知っておくとAPI Referenceの読み込みが捗りそうなことをシェアします。
結論としては、単一のエンドポイントにSQLのような「クエリ」をPOSTして使うイメージを持っていればおkです。
なお、この記事は私が使ったCI/CDツールのHarnessのGraphQL APIの仕様をベースに書いていますので、必ずしもすべてのGraphQLには当てはまらないかもしれないことをご承知おきください。
とりあえずHTTPベースで使えるから安心しておk
GraphQLについて調べてみると「RESTに代わる新たなAPI規格!」などと紹介されていることもあって少し身構えてしまいますが、RESTとはエンドポイントの用意のされ方や、リクエストの投げ方が少し異なるだけで、HTTPベースで使えることには変わりがないので、安心しておkです。
厳密にはGraphQL自体はあくまでSQLのようなクエリ言語であり、データのやり取りさえできればどんなプロトコルの上でも実装可能だは思いますが、WebAPIで使われる場合はHTTPの上で実装されることが基本で、IoT周りでよく聞くMQTTのように、HTTPを置き換えてしまうというようなことはないです。
下記は利用者目線から見たRESTとの違いをまとめます。
違い① エンドポイントが固定
RESTの場合、APIが提供する機能ごとにエンドポイントが分かれています。例えば、ユーザを操作するAPIであれば、機能ごとに下記の様なエンドポイントが用意されているのをよく見ると思います。
RESTの場合のエンドポイントの例
ユーザ一覧取得: GET /api/users/
特定のユーザの情報を取得: GET /api/user/<user id>
ユーザ情報の更新: POST /api/user/<user id>
ユーザ作成: POST /api/createuser/
…
一方で、GraphQLの場合は、全ての機能で共通の、単一のエンドポイントが定義されます。
GraphQLの場合のエンドポイントの例
全ての機能共通で POST /api/graphql/
どのような機能を利用したいかは、全てリクエストボディの中に記載される「クエリ」によって指定します。SQLを投げるようなイメージで捉えればよいと思います。DBにSQLを投げるとき、どのテーブルにどんな操作をするかによって接続先は変わりませんよね。DBの接続先は共通で、その中でどのテーブルにどんな操作をするか?をSQLで指定するのと同じノリです。
また、RESTではHTTPメソッドが機能(エンドポイント)によってGETだったりPOSTだったりしますが、GraphQL APIの場合は情報の取得だろうが更新だろうが固定のエンドポイントにクエリを投げて行うため、常にPOSTです。
違い② 複数の命令を一つのリクエストの中で同時に投げることができる
RESTでは機能ごとにエンドポイントが分かれているため、複数の機能を利用する場合は複数のリクエストを投げる必要がありますが、GraphQLでは単一のエンドポイントに対して複数の命令(クエリ)を同時に投げることで、一つのリクエストで済ませることができます。
違い③ レスポンスデータの構造をリクエスト側で制御できる
基本的にRESTではエンドポイントごとにレスポンスの構造が決まっています。一方GraphQLでは、リクエスト時のクエリでレスポンスデータの構造を指定します。これによって、必要な項目だけを送受信することができます。下記はHarnessのユーザ情報の一覧を取得するクエリの例です。ユーザを最大10件、1件ごとにメールアドレスとIDだけを返します。nodes {} の中で指定した項目だけが返ってきます。
{
users(limit: 10) {
nodes {
email
id
}
}
}
違い④? 結果をチェックする際にレスポンスコードがあてにならない
これはもしかしたらHarnessのGraphQLだけかもしれません。HarnessのGraphQL APIでは、HTTPのレスポンスコードはクエリ構文に問題がなく無事にクエリが処理されれば、クエリ処理においてエラーが発生した場合であっても200番が返ります。例えば「ユーザIDが重複していてユーザを作成できなかった」というクエリ処理におけるエラーの情報は、レスポンスのjsonの中に含まれます。そのため、GraphQLを用いたAPIコールのクライアント側の処理としては、レスポンスコードのチェックだけでなく、常にレスポンスのjsonを見てエラーチェックしてあげる必要がありました。
GraphQLでは一つのリクエストで複数の命令を行うことができるため、一つのリクエストの中で、ある命令は成功したが、別の命令は失敗した、という状況が起こりえます。そのため、クエリ自体の結果をHTTPのレスポンスコードとして返すのはあまり良い実装ではないでしょう。そのため、おそらくHarnessだけでなく、多くのGraphQLベースのWebAPIにおいて、このような作りになっていると思います。
おわりに
初めてGraphQL APIを使ってみてわかったことをまとめました。他にも利用者目線で抑えておくべき違いがありましたら共有頂けるとありがたいです。