[セッションレポート] REST APIとGraphQLに入門 SVS314 API patterns and architectures: RESTful vs. GraphQL APIs #reinvent

2021.01.29

CX事業本部@大阪の岩田です。re:Invent 2020のWave 2で新たに公開されたセッション「SVS314 API patterns and architectures: RESTful vs. GraphQL APIs 」を聴講したのでレポートさせて頂きます。

セッション概要

スピーカー:George Mao

以下公式サイトからの引用です。

Join this session for a discussion on patterns you can use to build your serverless APIs. Learn about REST APIs powered by Amazon API Gateway and compare them to GraphQL APIs on AWS AppSync. Also, learn from use cases, learn best practices, and see how AWS helps developers build secure and scalable APIs.

サーバーレスなAPIを構築する際の選択肢としてAPI GWを利用したREST API、AppSyncを利用したGraphSQL APIそれぞれについて

学べるセッションとなっています。

API patterns and architectures: RESTful vs. GraphQL APIs

以下セッションレポートです。

REST APIとGraphQLの比較

  • API(Application programming interface)はバックエンドの実装を抽象化し、プログラミングをシンプルにする
  • APIのクライアントはバックエンドデータベースが何かを知る必要が無い
  • バックエンド側はテクノロジーやデータベースプラットフォームを自由に選定できる

  • RESTとGraphQLには多くの類似点がある
    • HTTP(S)上で動作する
    • クライアントとサーバー間はステートレスであり、接続を維持する必要がない
    • クライアントとサーバーの振る舞いは標準化されている

  • RESTは2000年頃から登場した概念で、多くのAPIプラットフォームで利用されている
    • ほとんどはJSON形式のペイロードを利用している
  • GraphQLは2015年にFacebookによって開発された技術
    • 2018年にGraphQL Foundationが設立され、GraphQLはOSS化した

  • この表はRESTとGraphQLのコンセプトを簡単に比較したもの
  • RESTでもGraphQLでもエンドポイントにはドメインやサブドメインを利用する
  • RESTではリソースパスをサーバー上のパスで表現するのに対して、GraphQLではリソースパスは常に/graphqlになる
  • クライアントからパラメータを指定する場合、RESTでは3つの選択肢がある
    • パスパラメータ
    • クエリストリング
    • HTTPペイロード
  • GraphQLでは常にGraphQLクエリを利用してパラメータを指定する
  • RESTはHTTPメソッドによってリソースに対する操作を指定するのに対して、GraphQLは常にPOSTメソッドを利用する

REST APIについて

  • REST APIはサーバー向けにデザインされている
  • REST APIは以下の組合せを利用してサーバーにリクエストを送る
    • リソースパスとオプションのパス
    • クエリパラメータ
    • HTTPメソッド (リソースに対して実行する操作を動詞として表現)
  • サーバーは静的に事前定義されたスキーマに準拠したペイロードを返却する

  • REST APIのクライアントはリソースに対して実行したい操作をHTTPメソッドで指定する
    • POST...レコードの作成、追加
    • GET...データのフェッチ、読み取り
    • PUT...データの上書き
    • PATCH...データの更新
    • DELETE....レコードやデータの削除
  • クライアントには正しいHTTPメソッドを選択する責務がある

  • 郵便番号を使用してレストランを検索するREST APIの具体例
    • 情報を取得するのでHTTPメソッドはGETを利用
    • api.anycompany.comのような形式でドメインを指定
    • リソースのパスは/restaurants
    • 郵便番号で検索するためにzipというクエリパラメータを利用
  • API GWからLambdaが起動し、DynamoDBから郵便番号が1111のレストラン一覧を取得して返却する

  • 郵便番号を指定してレストランの一覧を取得しつつ、各レストランのメニュー一覧を取得したい場合はどうするか?
  • 一例としてクエリストリングmenuyesをセットしてリクエストするという設計が考えられる
  • API GWからLambdaが起動し、Lambdaがmenu=yesというクエリストリングを解釈してDyanmoDBに適切なクエリを実行することで、レストランが提供可能なメニュー一覧をレスポンスできるようになる

  • 先ほど紹介した設計は唯一の選択肢ではない。レスポンスにメニュー一覧を返却するAPIのとしては以外のような設計が考えられる
    • クエリストリングで指定する.../restaurants?zip=11111&menu=yes
    • パスパラメータで指定する.../restaurants/11111/menu
    • 専用のリソースを定義する.../restaurantsWithMenu/11111
  • REST APIを構築するには複数の選択肢があり、クライアントはパスパラメータやリソースのパス、あるいはそれらの組合せについて理解しておく必要がある。

GraphQLについて

  • GraphQLは強く型付けされたクエリ言語で、クライアントから必要なデータを要求できるように設計されている
  • クライアントはリクエストボディにGraphQLのクエリをセットし、/graphqlというエンドポイントにPOSTメソッドでリクエストを送信する
    • サーバーはクライアントが指定したGraphQLのクエリに応じたレスポンスを返却する
  • 指定された郵便番号の天気を取得するGraphQL、指定された郵便番号のレストラン一覧を取得するGraphQLクエリの例はこのようなクエリになる

  • GraphQLの中心はスキーマ
  • スキーマ定義は3つの要素で構成される
    • タイプ(types)
    • クエリ(queries)
    • ミューテーション(mutations)
  • この例はレストランのタイプを定義している例

  • スライド左側はそれぞれレストランとメニューのオブジェクトタイプを定義する例
  • スライド右上はクエリの例
    • それぞれレストランの一覧を取得するクエリ、1件のレストランを習得するクエリの例
  • GraphQLではデータを変更する際にミューテーションを利用する
    • スライド右下のミューテーションはそれぞれレストラン作成用、削除用のミューテーションの例

REST APIとGraphQLそれぞれの具体的な動作

  • GraphQLの動作例
  • 郵便番号を指定してレストランの一覧を習得したい場合、クライアントはエンドポイント/graphqlに対してPOSTメソッドでクエリを送信する
  • AWSのAppSyncはサーバーレスなGraphQL APIを構築するためのマネージドサービス
    • AppSync resplverがバックエンドのデータソースから様々なデータを習得する
  • 全レストランの名前と住所が必要な場合は以下のようなクエリを実行する
query getAllRestaurants(zip) {
  getAllRestaurants(zip) {
    name
    address
  }
}
  • 追加でメニューの情報が必要であればクエリを以下のように更新する
query getAllRestaurants(zip) {
  getAllRestaurants(zip) {
    name
    address
    menu {
      name
    }
  }
}
  • さらにメニューの価格やグルテン有無が知りたければクエリを以下のように更新する
query getAllRestaurants(zip) {
  getAllRestaurants(zip) {
    name
    address
    menu {
      name
      price
      glutenFree
    }
  }
}
  • バックエンドはクライアントが必要な情報を理解し、適切なデータソースから情報を取得・返却する

  • API GWはREST APIを構築するためのマネージドサービス
    • 完全従量課金
    • さまざまなクライアントから利用可能
    • 多数のAWSサービスと統合可能
  • API GWでレストランのAPIを構築すると複数のリソースパスが必要になる
    • /restaurants
    • /restaurants/{zip}
    • /restaurants/{zip}/menu
    • /restaurantsWithMemu
    • /restaurantsWithMemu/{zip}
  • これらのパスへのアクセスは全てバックエンドのLambda Functionで処理される
  • API GWがリクエストを受信した際の動作例
    • /restaurantsにというエンドポイントにクエリストリングzipmenu付きのリクエストが送信された場合
    • API GWはリクエストを解析し、ペイロードフォーマット2.0という形式で下流のLambda Functionにペイロードを引き渡す
    • Lambdaはペイロードを解析し、バックエンドのDynamoDBに適切なクエリを実行してレスポンスを返却する

  • AppSyncはサーバーレスなGraphQL APIを構築するためのマネージドサービス
    • リゾルバを介してバックエンドのデータソースとスキーマをマッピングする
    • リゾルバはDynamoDB、Aurora、Amazon ES、Lambda、HTTPエンドポイントなどの様々なデータソースをサポートしている
  • このスライドの例ではレストランのスキーマに対して2つのリゾルバと2つのデータソースをマッピングしている
    • 1つ目のリゾルバはDynamoDBをデータソースとし、レストランのID、名前、住所...といった情報を取得する
    • 2つ目のリゾルバはLambdaをデータソースとし、レストランのメニュー一覧を取得する

  • AppSyncのクエリが動作する具体例
    • 全レストランの情報を取得するgetAllRestaurantsというクエリを例に考える
    • クライアントからgetAllRestaurantsが呼び出されるとAppSyncは要求されたプロパティとリゾルバのマッピングを行う
    • 名前、住所、州...といったプロパティはDynamoDBリゾルバが設定されているため、DynamoDBのresaurantsテーブルに対してスキャンを行い結果を取得する
    • レストランごとのメニュー一覧にはLambdaリゾルバが設定されているため、DynamoDBリゾルバから取得した各行に対してLambdaリゾルバを呼び出してメニュー一覧を取得する
  • バックエンドは2つの異なるリゾルバを利用しているが、クライアント側はそれを意識することはない。クライアントは単純に1つのHTTPリクエストを送信しているだけ

  • AppSyncのクエリが動作する具体例
    • クライアントがgetAllRestaurantsを呼び出しているが、メニュー一覧は要求されていない
    • この場合DynamoDBリゾルバがresaurantsテーブルをスキャンするだけでAppSyncクエリが完了する

  • 更新処理に関するREST APIとGraphQLの比較
  • レストランリソースを新しく追加する場合、REST APIではGETではなくPOSTを利用する
    • 追加する新しいレストランの情報をHTTPボディで指定する
  • これまで見てきたようにGraphQLは常に/graphqlというエンドポイントに対してPOSTを行う。これは更新処理でも同様
    • レストランを追加するためのミューテーションを指定し、RESTと同じようにHTTPボディでレストランの情報を送信する

REST API or GraphQLの選択

  • REST APIとGraphQL、チームでどちらのAPIを採用するかは大きな決定になる
  • 開発体験を考慮して決定する必要がある
  • REST APIにはJavaScriptのコードから簡単に呼び出し可能で、エコシステムも整備されている。
  • GraphQLはSDKを利用してAPIを呼び出す必要がある。周辺ツールとしてPOSTManはGraphQLをサポートしているが、REST APIほどのエコシステムは整備されていない

  • REST APIではレスポンスデータのサイズがサーバーサイドに依存するが、GraphQLではクライアント側が要求したクエリに基づいてデータのサイズが決まる。
  • REST APIではレスポンスのペイロード形式もサーバーサイドに依存する。GraphQLではクライアントが自由に要求できる。
  • REST APIはモダンブラウザでネイティブにサポートされるが、GraphQLにはJavaScriptのライブラリが必須となる。

まとめ

  • RESTとGraphQLどちらを採用すべきかの判断には多くの考慮事項がある。
  • データソースの数
    • 多数のデータソースがある場合、リゾルバをマップすることでデータソースを抽象化できるGraphQLが有利
  • データソースの変更頻度
    • リゾルバの変更でデータソースの変更に追従しやすいGraphQLがオススメ
  • APIクライアントは何か?どうやって制御するか?
    • GraphQLという技術はREST APIほど浸透していないため、GraphQLを利用するためにクライアントに開発費用を支払う必要があるかもしれない
  • 開発者のスキルセットは?
    • 開発者全員がREST APIを理解している場合、GraphQLに移行するメリットが薄くなる。

感想

レベル300ということで、あまり深い内容まで言及されていませんがREST APIとGraphQLの基礎を学ぶのにちょうど良いセッションではないでしょうか?個人的にはGraphQLやAppSyncを実案件に導入した経験が無いので早く使ってみたいです。