AppSync VS APIGateway 両者の違いをスタートアップCTOが本気で考えてみた!

AppSync VS APIGateway 両者の違いをスタートアップCTOが本気で考えてみた!

こんにちは!

最近は、AppSync/GraphQLでの開発案件が非常に増えてきました。

GraphQL は、RestAPI のようなリソース志向の API設計に対し、データアクセス志向な API設計を可能にします。つまり、フロントエンドの画面の振る舞いを起点にした API設計/開発を行うことができます。

また、2018年にAws API GatewayがWebSocket APIに対応しました。

それまでは AppSync のメリットはサブスクリプション通信可能であることが挙げられていましたが、API Gateway が Web Socket に対応したことで、両者に明確に機能面で優位性は無いように思えます。

そうなると AppSync と API Gateway どちらを採用すれば良いのか迷いどころだと思いますが、わたしたちの AppSync 開発の知見をベースに本記事で解説しようと思います。

想定する読者

  • Rest を脱却し GraphQL 開発に挑戦したいけど、いろんな事情で踏み出せないヒト
  • Rest で普段開発しているけど、GraphQL 開発が気になっているヒト
  • プロジェクトへ新しく AppSync を導入しようと検討しているヒト
  • Amplify ローカルで触ってみたけど実戦で使用するのが怖いヒト

はじめに

AppSync の概要

AppSync の役割は?

ズバッと言いますが、AppSync は GraphQL のエンドポイントを提供します。

API Gateway は複数のAPIエンドポイントを提供しますが、AppSync はGraphQL コードを受け付ける1つのエンドポイントしか提供しません。(1つのエンドポイントへ POST を受け付けるイメージ)

AppSync のメリット

AppSync をバックエンドにすると下記のようなメリットがあります。

  • GraphQL のスキーマにデータアクセスのバリデーション定義が可能
  • スキーマ定義と連携したキャッシュを設定可能
  • Cognito のユーザープールグループと連携した APIアクセス制限が可能
  • VTL でプログラミングレスで API構築可能( メリデメあります… )
  • フロントエンドと MQTT通信を提供してくれる( リアルタイム通信が容易に可能 )
  • Cognito ユーザープールと認証連携

AppSyncのデメリット

  • RestAPI に対して圧倒的に開発経験者が少ない
  • 既存のプロジェクトメンバーが抵抗を感じる可能性が高い(Restと開発の流れや開発方法が大きく異なります)
  • Amplify をクライアントに使用する場合は SSR出来ない( アポロSDKを拡張した AppSyncSDK であれば SSR可能だが実装が複雑になる可能性有 )

GraphQL と RestAPI の開発フローの違い

まず GraphQL の最大のメリットは、フロントエンドのエンジニアが手軽に API開発に参加できることです。

それぞれざっくり下記のような開発フローとなります。

GraphQL の開発フロー

  1. 画面仕様(デザイン)を作成
  2. 画面仕様からデータソース(DynamoDB)を作成
  3. データアクセスの GraphQL 及びマッピングテンプレートを作成
  4. テスト

RestAPI の開発フロー

  1. 機能仕様書からデータベース設計
  2. データベースへアクセスするための API設計
  3. APIのプログラミング
  4. テスト

両者の最大の違いは、「デザイン起点設計」と「機能起点設計」です。

GraphQL の開発では、まず画面の振る舞いを決定し、そこからどのようなデータアクセスが必要か設計します。( DynamoDBと非常に相性が良いですね

GraphQL は、デザイン駆動で作り始めるのがポイントです。

逆に言うとデザイン上に必要ないデータアクセスはスキーマ定義不要ですし、適切にデータアクセスを設計すれば、フロントエンドから一度の通信で全てのリソース情報を取得することも可能です。( DOMのマウント時に複数APIをコールして…..なんてことが軽減されます )

AppSync と API Gateway のユースケース

まず、GraphQL と RestAPI どちらを採用するか決めましょう

GraphQL を採用したいケース

  • デザイン駆動型の開発
  • アジャイル開発で要件がバシバシ変わる
  • DBMS は DynamoDB である(データアクセス志向なDBMSを採用)
  • リーンスタートアッププロジェクト

RestAPIを採用したいケース

  • 堅牢なシステムでテスト駆動型を必要とする場合
  • ウォーフォール開発で要件かっちり
  • データアクセス志向ではない DBMS(RDB等)
  • 業務委託で外出しで開発を行う場合
  • GraphQL エンジニアを確保できない場合(無理に使用するとキャッチアップコストが高くなり返って疲弊します)
  • 外部のサービスと API連携するのを前提とするシステム

GraphQL のときは AppSync、Rest のときは API Gateway

わたしたちは前述のようにGraphQL と AppSync どちらを採用するか検討した後、GraphQL の場合はAppSync、Rest の場合はAPI Gateway を採用しています。

ただし、注意点なのは全ての APIを GraphQL で作れるとは限らないということです。例えば「外部提供API」の開発要件が発生した場合、第三者に GraphQL でコールしてくれと言ってもなかなか受け入れてくれませんので、RestAPI の口を個別に作る必要があります。(基本的には GraphQL に閉じて開発は行えます)

AppSync へ接続するクライアントには何を採用すれば良いのか?

インターネットで調べると、Amplify 関連の記事がかなり多いと思います。Amplfiy は AWS社としても1推しの GraphQL クライアントなので、迷ったらAmplify を採用して問題ないです。

ただし下記のようなケースでは Amplify ではなく AppSyncSDK を利用する必要があります

  • Amplify でSSR したい、SSR が重要なアプリケーション
  • Appolo での開発経験者がプロジェクトに多い

AppSyncSDK は Apollo を拡張したライブラリです、つまり Apollo に対して知見が高ければ AppSyncSDK を使用するのも1つだと言えます。(ほぼ Apollo のドキュメントを見て開発が可能なので慣れている人にとってはメリットが大きい)

AppSyncのデメリットについて深堀

わたしたちが感じる AppSync のデメリットは大きく2つあります。

  • マッピングテンプレートの記述が独特なため慣れるまで大変
  • CRUD への単体テストが書きにくい

特にマッピングテンプレートは慣れないとなかなか苦戦するかと思います。(シンタックス自体はシンプルなので、少し文法を抑えれば大丈夫ですが…)

例えば、AppSync → ElasticSearch のマッピングテンプレートでは、下記のようなコードになります。

## [Start] Determine request authentication mode **
#if( $util.isNullOrEmpty($authMode) && !$util.isNull($ctx.identity) && !$util.isNull($ctx.identity.sub) && !$util.isNull($ctx.identity.issuer) && !$util.isNull($ctx.identity.username) && !$util.isNull($ctx.identity.claims) && !$util.isNull($ctx.identity.sourceIp) && !$util.isNull($ctx.identity.defaultAuthStrategy) )
  #set( $authMode = "userPools" )
#end
## [End] Determine request authentication mode **
## [Start] Check authMode and execute owner/group checks **
#if( $authMode == "userPools" )
  ## [Start] Static Group Authorization Checks **
  #set($isStaticGroupAuthorized = $util.defaultIfNull(
            $isStaticGroupAuthorized, false))
  ## Authorization rule: { allow: groups, groups: ["admin"], groupClaim: "cognito:groups" } **
  #set( $userGroups = $util.defaultIfNull($ctx.identity.claims.get("cognito:groups"), []) )
  #set( $allowedGroups = ["admin"] )
  #foreach( $userGroup in $userGroups )
    #if( $allowedGroups.contains($userGroup) )
      #set( $isStaticGroupAuthorized = true )
      #break
    #end
  #end
  ## Authorization rule: { allow: groups, groups: ["customer"], groupClaim: "cognito:groups" } **
  #set( $userGroups = $util.defaultIfNull($ctx.identity.claims.get("cognito:groups"), []) )
  #set( $allowedGroups = ["customer"] )
  #foreach( $userGroup in $userGroups )
    #if( $allowedGroups.contains($userGroup) )
      #set( $isStaticGroupAuthorized = true )
      #break
    #end
  #end
  ## [End] Static Group Authorization Checks **

  ## No Dynamic Group Authorization Rules **

...

$util.toJson($context.result)

Cognito のユーザーグループを判別して利用できる API かチェックして上で、処理を実行しているサンプルです。(レスポンスマッピングテンプレート)

ただし、実は基本的なマッピングテンプレート(例えば Dynamo, ElasticSearch へのデータ出し入れ)は、AppSync コンソールで自動的に生成したコードを少し修正すれば手軽に作成することが可能です。慣れるまでは、AppSync コンソールと仲良くなっておくのも1つでしょう。

まとめ

最近はUXに優れた WEBサービスが非常に多く、開発者は競合サービスに勝つために日々新しいUXを考えプロダクト改善するなんてことが多いのではないでしょうか。

データアクセス(UX)を起点として開発できるのが、AppSync/GraphQLの魅力ですね。

わたしたちは、GraphQL の開発プロジェクトを強力にサポートします。お気軽にお声掛けください。