AppSyncとGraphQLとは?re:Invent 2020のセッションをもとにまとめてみた

こんにちは。てぃろです。

この記事では、re:Invent2020で行われたAppSynceに関するセッションの内容をもとにしながら、AppSyncとGraphQLについてまとめています。

多分に自分の勉強のためではありますが、私と同じように以下のような思いをお持ちの方に読んでいただければと思います。

  • GraphQLって何?APIなの?
  • REST APIとどう違うの?
  • AppSyncって何?API Gatewayとどう違うの?

これらの問いに対する回答を簡単にしてしまうと、以下のようになります。

  • GraphQLとは、Web APIの実装方式一つ
  • RESTとGraphQLの違いは、APIリクエストの表現方法と考え方が違う
  • AppSyncとは、GraphQL実装のフルマネージドサービス

この記事ではこれらの回答になる内容を、セッションの内容を補足しながら紹介していきます。

なお、ここで記載する内容のセッションは2021/1中はまだ以下からご覧いただけます。

Power modern serverless applications with GraphQL and AWS AppSync

GraphQLとはWeb API用のクエリ言語である

まず、AppSyncのいいところを見ていくためにも、GraphQLについて理解しましょう。

GraphQLとはクエリ言語である。

って言われても正直ピンとこないと思います。REST APIと比較して違いを並べてから考えてみます。

  • REST
    • HTTPメソッド:GETやPOSTなどで意味を持たせて使う
    • パラメータ:URL、query string、bodyを使い分ける
  • GraphQL
    • HTTPメソッド:POSTだけ
    • パラメータ:bodyだけ

そもそもAPIはサーバに処理を要求するためのメッセージを送るために使います。そのメッセージの表現力をできるだけ高めるためにRESTではHTTPメソッドや各種パラメータに意味を持たせて使っていました。

しかし、RESTのやり方では実装が複雑だったり、標準的な方式があるわけでもなくて実装が人によってブレブレだったり…。加えて、無駄なデータの授受がある(後述)などの実際的な問題点もあります。

対してGraphQLはJSON形式のクエリをPOSTメソッドで送信するだけ、という非常にシンプルなWeb APIの実装方式の一つとして作られました。リクエストの方法はシンプルですがJSON形式のクエリによる柔軟な表現が可能です。

次に、GraphQLの利点の一つとして無駄なデータの授受がない点を見てみます。

以下のようにRESTによる実装方法だと、同じ一つのAPIでモバイル用もWeb用のデータも含めたレスポンスを返すことが多いと思います。(実装の工夫次第ではRESTでもこの限りではないですが、一般に実装コストの面から言って、一緒になっていることが多いです)

つまり、モバイルユーザにとってはWeb用のデータは無駄。Webユーザにとってはモバイル用データが無駄なわけです。

しかし、GraphQLでは過不足なく必要なデータだけ取得するということが(容易に)可能になります。

これはGraphQLがクエリ言語であることによる恩恵です。クエリでクライアント側が必要なデータを細かく指定してリクエストすることが容易になります

次にGraphQLの操作についてみてみます。操作は3種類定義されています。

  • Queries
    • データを取得する操作を要求する
    • RESTでいうところの、GETの参照系APIに相当
  • Mutations
    • データの変更を伴う操作を要求する
    • RESTでいうところの、POST・PUT・DELETE等の更新系APIに相当
  • Subscriptions
    • サーバーサイドから同期通信を受けるなどのリアルタイム処理で使用する操作を要求する
    • RESTにはこれに相当するものはない

このようにやっていることはシンプルなように見えますが、実際JSON形式のクエリを解釈するなど自分で実装するのは大変です。そこでAWSのサービスであるAppSyncを使いましょう。

AWSのGraphQLサービス:AppSync

AppSyncはAWSが提供するフルマネージドサービスの一つで、GraphQLを扱うことができるAPI Gatewayのようなサービスです。

データソースとしてDBやLambda、EventBridgeといったAWSのサービスだけでなく、HTTPリクエストや、Pub/Subなど様々なものが使うことができます。

AppSyncはGraphQLの形式でクライアントからの要求を受け取り、これら多くのデータソースから適切なデータを収集してレスポンスする、というわけです。

そんなAppSyncは、Data modelを扱うSchemaとビジネスロジックを扱うResolverからなります

  • Schema:データモデル、外向けにどんなJSONでやりとりするかを表す
  • Resolver:データソースへアクセスし、データの処理を行う部分

つまりは、Schemaの形でリクエストを受け取り、Resolverがリクエスト内容を解釈して必要なデータをデータソースから取り出す/書き込む、ということです。

この大事なResolverは2種類あります。

  • Unit Resolver(ユニットリゾルバー):単一の処理を行うリゾルバー
  • Pipeline Resolver(パイプラインリゾルバー):複数の処理を行うリゾルバー

単一のデータソースへのアクセスなど単純な処理の場合には、Unit Resolverを使えばOKです。

Pipeline Resolverは、複数の処理を直列に行う必要があるときに使います。Resolverそれぞれが別々のデータソースへのアクセスが可能なため、様々なデータを統合してクライアントへレスポンスすることができます。

他にも、AWSのサービスということで、直接Lambdaを呼び出すこともできます。この場合は特にLambdaリゾルバーといいます。

このようにしてAppSyncは、データソースやデータの統合といったバックエンドの複雑性を隠ぺいすることが容易にでき、クライアントはシンプルにGraphQLで柔軟なクエリを使いバックエンドへアクセスができるというわけです。

AppSyncの認証と認可

AppSyncは複数の認証モードをサポートしています。

  • APIキー
  • Amazon Cognito User Pools
  • OpenID Connect
  • AWS IAM

図にはLambdaも載っていますが、公式ドキュメントにある通り認証モードは上記の4つだけです。

Lambdaに関してはPipeline Resolverと組み合わせカスタム認証として使用することも可能とのことで、認証方式の一つとして上図に載っています。

これらの認証モードは複数同時に定義することができます。複数同時に定義すると認証方法が増えるというだけではありません。

それぞれの認証に応じてどのデータを出力してよいかを細かく制御できます。認可と同じようなイメージと言ってよいかと思います。

下図の例のように定義することができます。

図の色分けは、以下のように3種類の認証が同時に使われていることを表しています。

  • オレンジ:APIキー
  • 緑:Cognito
  • 青:IAM

それぞれの認証に応じて、取得できるデータが変わってきます。つまり、APIキー認証を使った場合はオレンジのデータが取得できますが、青のデータは取得できません。

この機能については公式ドキュメントではかなりさらっと書いてありました。注意深く読んでみてください。

複数認証の実装イメージをよりつかみたい方は以下の記事を参照して下さい。

Subscriptionsによるリアルタイム双方向通信

AppSyncの持つ操作の最後の一つである、Subscriptionsの話です。

Subscriptionsは、websocketを活用したリアルタイム双方向通信を実行することができる優れた機能です。

GoogleのFirebaseなどでもリアルタイムDBがありますが、それと似たようなことができるようです。

Subscriptionsを使うには、まずクライアントからsubscription callをします。これで24時間のセキュアなコネクションが確立されるので、その間は通信が保たれます。

他のクライアントから同じようにSubscriptionsを実行すると、同じwebsocket channelが使用されます。つまり、複数のクライアントで同じデータソースを監視し続けられるわけです。

データソースへの変更が検知されると、その変更が同じwebsocket channelを使っているクライアントに自動的にブロードキャストされます。つまり、どれか一つのクライアントがデータを変更すると、その内容がその他のクライアントにも送られます。このようにして自動的にデータの同期が行われます。

しかし、当然ですがデータソースを直接変更しても変更は検知されません。変更検知をしてほしいのであれば、AppSyncを通す必要があります。

ただし、アーキテクチャ上必ずAppSyncを通すのがよくない場合もあります。そんなときは変更したことをAppSyncに別途通知すればOKです

例えば、DynamoDBの場合、データ更新後にDynamoDB StreamでLambdaを起動し、変更したことをAppSyncに伝えるといったことが可能です。

これにはLocal Resolver(ローカルリゾルバー)を使います。これによってDBへの二重書き込みなどすることなく、変更検知のみが可能となります。

公式ドキュメントのチュートリアルでは、データソースを介さずクライアントに通知を送るために使うというケースで使われていました。

AppSyncのオフライン機能

AppSyncはSDKを使うことで、ネットワークが切れたときにはキャッシュしておいたデータを使って動作することが可能です。

もしデータの変更を入れた場合には、その変更をローカルに貯めておき、ネットワークが回復したときに自動的に送信してデータ同期するという仕組みです。

AppSyncが連携できるサービス

言わずもがな、AppSyncはAWSのマネージドサービスなのでそのほかのマネージドサービスと統合されています。

ここで紹介されているのは、WAF、X-ray、CloudWatch、ElastiCacheです。今後も連携できるサービスは追加されるでしょうし、その連携機能もどんどんリッチになっていくと期待できます。

AppSyncの開発アプローチは2種類ある

セッションの中では、以下の2種類の開発アプローチがあると紹介されていました。

  • Schema First:スキーマを最初に作ってそれを正として作っていく
  • Code First:コードから書いて動的にスキーマを作っていく(コードの重複を防ぎやすくなる

これはサービスの機能というより、開発方法論なので好きなやり方をすればいいと思います。参考程度に見ておき、好きなやり方を選んでいくとよいのではないでしょうか。

まとめ

AppSyncはGraphQLを扱うAPI Gatewayのようなサービスです。

ですが、API Gatewayとは違う様々な特徴(リアルタイム通信やオフライン機能)を持っており、適用すべきユースケースも異なってくると思われます。

GraphQL自体がRESTほど普及していないことも事実です。REST APIより優れた部分が多いことがわかる一方、世の中で広く使われるにはまだまだ時間がかかる可能性もあります。

それでも、GithubもGraphQLを使ったAPIを公開するなど採用事例は増えています。今後いつRESTからの乗り換えが起きてもいいように、新たな技術として学んでおくことが必要ですね。

参考文献

re:InventのLambdaのセッションに関してもまとめています。ぜひこちらもご覧下さい。