Open4

AWS AppSyncを触った雑感想

yaguchiiyaguchii

はじめに

  • 私はHasuraを利用しているのだが、AppSyncはよくHasuraと比較されるため以前から気にはなっていた
  • 2023/2からHasuraのPricingが変更されたため、有料プランが従来の$99→$1.50/hour ($1,080/month)という料金になりめちゃくちゃ利用ハードルが上がった

AWS AppSyncとは

  • 2018/4にGAになった、AWSのマネージドなGraphQL APIサービス
    • Hasuraが設立されたの2017(ってどこかで見た)から、そのちょっとあとにGAになったのか
  • マネージドサービスなので、自前でサーバ管理する必要はない
  • DynamoDBテーブルやLambda関数などをデータソースとしたAPIを作成できる
yaguchiiyaguchii

料金

  • リクエスト数単位での課金
    • API Gatewayと同様のイメージ
    • 別途EC2のインスタンス費用などは掛からない
  • データソースにRDSやDynamoDBやLambdaを使っていたりしたら、それぞれ課金される
  • 以下公式ドキュメントに書いてあった料金例

例) あるブログアプリケーションに月間 50,000 人のアクティブユーザーがいて、それぞれ 100 回の検索を実行するとします。検索によって毎月 5,000,000 件の AppSync クエリ操作が発生し、レスポンスサイズは平均 3 キロバイト (KB) です。

- クエリ操作料金	500 万回 × 4.00 USD/100 万操作 = 20.00 USD
- データ転送料金	3 KB × 500 万回 = 1500 万 KB = 14.3 GB × 0.09 USD = 1.29 USD

AppSync 料金の合計	20.00 USD + 1.29 USD = 21.29 USD
yaguchiiyaguchii

雑感想

  • Hasuraは初期立ち上げのコストが圧倒的に低い
    • Hasuraだと、接続したデータベースに変更を加えるだけでGraphQL schemaやresolverを生成してくれる
  • 一方、AppSyncだと、schema.graphqlを定義し、mapping templateを記述するっていうのは最低限必要
    • もちろんページングや集計クエリも自前で定義する必要あり

mapping templateとは?

client <-> query / mutation <-> mapping template(※コレ) <-> resolver <-> data source
  • clientからのリクエストをresolverに渡すための変換処理が書かれたファイル
  • VTL(Velocity Template Language)というテンプレート言語で記述する
    • VTLはJavaで使われるテンプレートなので、AppSyncはJavaで動作していると想像...
    • コード補完が効かないし、処理の共通化ができなかったりと辛そう
    • 色々なブログみても、VTLはAppSyncのデメリットに必ず挙げられてる

VTL

たとえばこんな感じ。
mapping templateという名の通り、値の詰め替えやデフォルト値の設定なんかをする。
バリデーションチェックもここでやっても良い。

schema.graphql
type Task {
  id: ID!
  title: String!
  description: String!
  status: String!
  createdAt: String!
  updatedAt: String!
}

type Query {
  listTasks(
    limit: Int
    nextToken: String
  ): ListTasks
}
Query.listTasks.request.vtl
#set( $limit = $util.defaultIfNull($context.args.limit, 10) )
#set( $ListRequest = {
  "version": "2017-02-28",
  "limit": $limit
} )

#if( $context.args.nextToken )
  #set( $ListRequest.nextToken = $context.args.nextToken )
#end


#if( !$util.isNull($modelQueryExpression) && !$util.isNullOrEmpty($modelQueryExpression.expression) )
  $util.qr($ListRequest.put("operation", "Query"))
  $util.qr($ListRequest.put("query", $modelQueryExpression))

  #if( !$util.isNull($ctx.args.sortDirection) && $ctx.args.sortDirection == "DESC" )
    #set( $ListRequest.scanIndexForward = false )
  #else
    #set( $ListRequest.scanIndexForward = true )
  #end

#else
  $util.qr($ListRequest.put("operation", "Scan"))
#end

$util.toJson($ListRequest)
Query.listTasks.response.vtl
{
    "tasks": $util.toJson($ctx.result.items),
    "nextToken": $util.toJson($util.defaultIfNullOrBlank($context.result.nextToken, null))
}

  • VTLは辛そうだけど、VTLさえ書けばDynamoDBへの出し入れはできてしまうというのは良い
    • ちなみにこのVTLでデータソースと直接やりとりできるのはDynamoDBのみ
    • それ以外のデータソース(RDSやOpenSearch)の場合は、Lambdaなどを挟んでデータベースとのCRUD処理を書く必要がある
    • トランザクションが必要なmutationの場合は、データソースが何にせよLambdaなどで受けて処理する必要がありそう
  • VTLの話は長くなるのでこのへんでおしまい
  • ローカル開発環境は、Serverless frameworkのserverless-appsync-simulatorが使える
    • Lambdaのローカル起動もserverless-offlineというプラグインが使えるし、DynamoDBはserverless-dynamodb-localがあるし、Serverless framework便利や...
  • LambdaだけならAWS SAMを使ってインフラ管理してデプロイするのが楽だけど、AppSyncには対応していなそうなので、Serverless frameworkを使うのが一番簡単そう
yaguchiiyaguchii

まとめ

  • (あたりまえだけど)GraphQLサーバとしてどれを使うかは適材適所
  • とはいえ既にAWSを使っていて、GraphQLサーバを立てたいときの第一候補にはなりそう
    • リクエストベースの課金は非常にありがたく、AppSync + DynamoDB + Lambdaのようなアーキテクチャにすれば、かなりの低コストでGraphQLサーバを建てられちゃう
  • Serverless flameworkのプラグイン (serverless-appsync-plugin, serverless-appsync-simulator) を使えば、ローカル開発やデプロイ周りも割と楽にできそう
    • AWS CDK, CloudFormationでもインフラをコード管理できると思うが、ローカル開発の文脈でしんどそうなのでServerless使うのが良さそう
  • 認証認可周りはまだ触ってないので、いつか試したい
    • AWS Cognitoと連携することで開発体験的に楽になるのか