AWS AppSyncとGraphQL API

AWS AppSyncのGraphQLスキーマ、Query・Mutation・Subscription、データソースとResolver、DynamoDB連携、リアルタイム配信、オフライン同期を学びます。

学習順Step 79 / 203サービスAppSync試験ドメインパフォーマンス

AWS AppSyncとGraphQL API

AWS AppSyncは、GraphQL APIを構築・運用するためのフルマネージドサービスです。GraphQLスキーマでAPIが扱う型と操作を定義し、クライアントは必要なデータ項目を指定して問い合わせます。Resolver(リゾルバー)がGraphQLの各フィールドをデータソースへ接続するため、DynamoDB、AWS Lambda、Aurora、OpenSearch Service、HTTPエンドポイントなど、複数のバックエンドを一つのAPIから扱えます。

AppSyncはAPI Gatewayの別名や単なるREST APIの置き換えではありません。HTTP APIやREST APIのリソース・メソッドを中心に設計するAPI Gatewayに対し、AppSyncはGraphQLの型、フィールド、選択するデータ項目を中心に設計します。リアルタイム購読やクライアントのオフライン同期を必要とするアプリケーションも構築できますが、オフライン同期には対応するクライアント機能やサーバー側の同期設定が必要です。

たとえば、複数の利用者が宅配ロッカーの空き状況を確認するアプリケーションでは、AppSyncのGraphQL APIからロッカー情報を検索し、利用状況を更新できます。画面を開いた利用者が更新を購読していれば、空き状況の変更をリアルタイムに受け取れます。

AppSyncのGraphQL要求がResolverとデータソースを介して処理され、Subscriptionへ配信される流れ

GraphQL APIのスキーマと操作

GraphQLは、クライアントが必要なデータを宣言して取得するためのクエリ言語と、その要求を実行する仕組みです。Facebook(現在のMeta)が開発し、オープンソースとして公開しました。API提供者はスキーマでデータ型と操作を定義し、クライアントはそのスキーマに従ったGraphQLドキュメントを送ります。

GraphQLでは、一般に次の3種類の操作をスキーマで定義します。

操作 役割 RESTで近い用途の例
Query データを読み取る GETによる読み取り
Mutation データを作成・更新・削除する POST、PUT、PATCH、DELETEによる変更
Subscription 対象の変更をリアルタイムに受け取る RESTの単発要求・応答とは異なる継続的な購読

この対応は操作の目的を理解するための目安です。GraphQLのMutationがHTTPのPUTにそのまま対応するわけではありません。GraphQLはスキーマ上の操作名と引数で要求の意味を表し、通常のQueryやMutationはGraphQLエンドポイントへのHTTP要求で実行します。SubscriptionはAppSyncのリアルタイム接続を通じてデータを受け取ります。

次の例では、ロッカーの状態を読み取るQuery、空き状況を変更するMutation、変更を受け取るSubscriptionを定義しています。

type Locker {
  lockerId: ID!
  availableCompartments: Int!
  updatedAt: AWSDateTime!
}

type Query {
  getLocker(lockerId: ID!): Locker
}

type Mutation {
  updateLockerAvailability(
    lockerId: ID!
    availableCompartments: Int!
  ): Locker
}

type Subscription {
  onLockerAvailabilityChanged(lockerId: ID!): Locker
    @aws_subscribe(mutations: ["updateLockerAvailability"])
}

ID!やInt!の末尾の ! は、値が必須であることを示します。QueryとMutationのフィールドがAPIの入口となり、SubscriptionフィールドはどのMutationによる変更を購読するかを指定します。@aws_subscribeディレクティブでMutationとSubscriptionの対応を宣言しています。

必要なフィールドだけを要求する

GraphQLではクライアントがレスポンスに含めるフィールドを選択します。たとえば、画面にロッカー番号と空き区画数だけを表示するなら、次のように問い合わせます。

query GetLocker {
  getLocker(lockerId: "locker-42") {
    lockerId
    availableCompartments
  }
}

サーバー側のスキーマに updatedAt など他のフィールドが定義されていても、選択セットに含めなければそのフィールドはこの応答に含まれません。この性質により、クライアントは画面に必要なデータを一つのGraphQL要求で組み立てやすくなります。一方、どのフィールドを選べるかはスキーマで定義され、バックエンドから返る値はResolverの実装に依存します。

REST APIでは、リソースごとのエンドポイントとHTTPメソッドを設計し、応答データの形はAPIごとに決めます。GraphQLではスキーマがデータ型・操作の契約となり、要求ごとに選択セットを指定します。どちらが常に優れているという関係ではなく、API利用者、キャッシュやHTTP機能の要件、リアルタイム配信、既存のバックエンド構成などから選択します。

Mutationでデータを変更する

クライアントはMutationの引数として変更対象と新しい値を指定します。GraphQLドキュメントでは、操作名、フィールド名、引数、Mutationの結果から取得したいフィールドを記述します。

mutation UpdateLocker {
  updateLockerAvailability(
    lockerId: "locker-42"
    availableCompartments: 3
  ) {
    lockerId
    availableCompartments
    updatedAt
  }
}

この要求は、updateLockerAvailabilityフィールドに対応するResolverを実行します。ResolverがDynamoDBへ書き込む設計なら、引数をDynamoDBのキーと属性に対応づけ、テーブルを更新します。処理結果はGraphQLの応答として返されます。GraphQLのMutationは作成・更新・削除などの変更操作を表しますが、具体的な書き込み方法や検証規則はResolverおよびバックエンド側で定義します。

Resolverとデータソースの役割

Resolverは、GraphQLスキーマのフィールドとバックエンドの処理をつなぐ部品です。クライアントから届いたフィールド名や引数、認証済みユーザーの情報などを使ってデータソースへの要求を組み立て、データソースの応答をGraphQLの型に合わせて返します。フィールドにResolverが設定されていなければ、そのフィールドをデータソースへ問い合わせる処理は行われません。

GraphQLフィールドからResolverを経てDynamoDBへ要求し、結果をクライアントへ返す処理

AppSyncでは、Resolverから接続するバックエンドをデータソースとして設定します。主な接続先にはDynamoDB、Lambda、Aurora、OpenSearch Service、HTTPエンドポイントなどがあります。処理が一つのデータソースに対応する場合は単一のResolverを使い、複数の処理を順番に実行する場合はPipeline ResolverでAppSync Functionsを組み合わせられます。

データソースとResolverは役割が異なります。データソースはAppSyncが接続するバックエンドの設定であり、Resolverは特定のGraphQLフィールドについて要求と応答を変換する処理です。APIの一つのスキーマに異なる種類のデータソースを含められるので、クライアントには一つのGraphQL APIとして公開しながら、裏側で複数のサービスを利用できます。

DynamoDB Resolverで書き込む

AppSyncからDynamoDBへ直接接続する構成では、ResolverがGraphQLの引数をDynamoDBの操作に変換します。次はJavaScript Resolver(APPSYNC_JSランタイム)で項目を更新する処理の形です。

import { util } from "@aws-appsync/utils";

export function request(ctx) {
  return {
    operation: "UpdateItem",
    key: util.dynamodb.toMapValues({
      lockerId: ctx.args.lockerId,
    }),
    update: {
      expression: "SET availableCompartments = :count, updatedAt = :updatedAt",
      expressionValues: util.dynamodb.toMapValues({
        ":count": ctx.args.availableCompartments,
        ":updatedAt": util.time.nowISO8601(),
      }),
    },
  };
}

export function response(ctx) {
  return ctx.result;
}

request関数はGraphQLの引数からDynamoDBの UpdateItem 要求を組み立てます。keyにはテーブルの主キーを指定し、更新式で対象属性を設定します。response関数はDynamoDBの結果をGraphQLフィールドの戻り値として返します。実際に使用するテーブルのキー定義、属性、Resolverの戻り値の型はスキーマと一致させる必要があります。

AppSyncではVTL(Apache Velocity Template Language)を使うResolverも利用できます。既存APIにはVTLのマッピングテンプレートが使われていることがありますが、AWSの現行ガイドではJavaScriptのAPPSYNC_JSランタイムが主な開発方法として案内されています。VTLが使われている既存構成を理解する知識は役立ちますが、新規実装では要件と対応機能を確認してランタイムを選びます。

Lambda Resolverを選ぶと、ResolverからLambda関数へ処理を渡せます。関数内で複雑な業務ルールを実装したり、複数のAWS SDK操作や外部サービス呼び出しを行ったりできます。その分、Lambdaの実行時間やエラー、実行ロール、呼び出しコストなども設計対象になります。単純なDynamoDB操作ならAppSyncのDynamoDB Resolverを使うことで、専用の中継コードを減らせます。

Subscriptionで変更をリアルタイムに配信する

AppSyncのGraphQL Subscriptionは、対応づけられたMutationの実行を契機として購読者へイベントを配信します。クライアントはSubscriptionを登録して接続を維持し、Mutationが正常に実行されたときに変更情報を受け取ります。AppSyncはこの通信にWebSocket接続を使用します。

クライアントがロッカーの空き状況の変化を受け取るSubscriptionを開始する例です。

subscription WatchLockerAvailability {
  onLockerAvailabilityChanged(lockerId: "locker-42") {
    lockerId
    availableCompartments
    updatedAt
  }
}

Subscriptionフィールドは、スキーマの @aws_subscribe により updateLockerAvailability Mutationに関連づけられています。アプリケーションがAppSync APIに対してMutationを実行すると、そのMutationに対応する購読者へイベントが送られます。購読者はSubscriptionの引数で lockerId を指定し、特定のロッカーに関係する通知に絞り込めます。アクセス制御が必要な場合は、Subscription接続時の認証に加えてフィールド単位の認可やResolverによる検査を設計します。

Subscriptionで受け取るデータはMutation実行時に選択されたフィールドにも関係します。購読者へ届けたい項目がMutationの選択セットに含まれているかを確認してください。データベースをAppSync以外から直接更新しただけでは、その書き込みがGraphQL Mutationを通らないため、対応するSubscriptionが自動的に実行されるわけではありません。外部更新も配信したい場合は、AppSync APIを通したMutationを呼び出すなど、購読者へ通知する経路を別途用意します。

オフライン利用とデータ同期

モバイル端末やネットワークが不安定な環境では、データを端末側に保持し、接続が戻ったあとでクラウドと同期したい場合があります。AppSyncを利用したオフラインファースト構成では、Amplify DataStoreのようなクライアント側のデータ同期機能を組み合わせられます。クライアントのローカルデータ、AppSync API、データソースの間で変更を同期し、複数クライアントの更新が競合した場合の扱いを設計します。

AppSyncのバージョン管理されたデータソースとDelta Syncを構成すると、最初にベースデータを取得し、その後は前回同期以降に変更された差分を取得できます。データソース側では変更バージョンなどの管理が必要です。オフライン中に蓄積した変更を再接続後に反映する際は、競合の検出と解決方針も定めます。

したがって、「AppSyncを使えば、任意のGraphQL APIが自動的にオフライン同期を行う」とは限りません。ローカルストレージを使うクライアント、同期を行うライブラリ、対応するResolver・データソース設定、競合解決の要件を合わせて構成します。リアルタイムSubscriptionによる即時通知と、切断中の変更を後から同期する機能は別の仕組みです。

認証・認可の選択肢

AppSyncでは、GraphQL APIに対する認証方式として次の方式を選べます。APIの既定方式に加えて追加の認証モードを設定し、クライアントやフィールドに応じて複数方式を使う構成も可能です。

認証方式 主な用途
APIキー 公開データや開発時など、共有キーを使うAPIアクセス
AWS IAM IAM認証情報とSigV4署名によるAWS主体からのアクセス
Amazon Cognito User Pools Cognitoユーザーのサインイン状態に基づくアクセス
OpenID Connect 外部のOIDC準拠プロバイダーによる認証
AWS Lambda Lambda関数で独自の認証ロジックを実行

認証方式を設定することと、すべてのユーザーがすべてのデータへアクセスできることは別です。フィールド単位の認可、Resolver内での利用者情報・引数の検査、データソース側のIAM権限などを組み合わせ、誰がどの操作を実行できるか決めます。たとえばDynamoDBをデータソースにする場合、AppSyncサービスロールには対象テーブルと必要な操作に限定した権限を与えます。

AppSyncとAPI Gatewayを選び分ける

要件 検討するサービス・機能
GraphQLスキーマから複数のデータソースへアクセスしたい AWS AppSync
クライアントが必要な応答フィールドを選びたい AWS AppSyncのGraphQL Query
Mutationに連動するリアルタイム更新が必要 AWS AppSyncのSubscription
REST/HTTP APIのリソース、ルート、HTTPメソッドを公開したい Amazon API Gateway
HTTP APIから独自の業務処理を呼び出したい API GatewayとLambdaなどの統合
オフライン期間の変更を後から同期したい AppSyncにAmplify DataStoreやDelta Syncなどを組み合わせる設計

AppSyncはGraphQL APIを中心にしたマネージドサービスであり、API GatewayはHTTP APIやREST APIなどを公開するサービスです。GraphQLのスキーマ、複数データソースの統合、Subscriptionによるリアルタイム配信が要件ならAppSyncが候補です。既存のREST契約、HTTPルーティング、API Gateway固有の機能を中心にするならAPI Gatewayを検討します。

DVA試験で押さえるポイント

  • AppSyncはGraphQL APIを管理し、スキーマ、Resolver、データソースを結びつける。
  • Queryは読み取り、Mutationはデータ変更、Subscriptionは変更通知の購読に使う。RESTのHTTPメソッドに完全に一対一対応するわけではない。
  • GraphQL要求では選択セットにより応答フィールドを指定できる。APIが公開する型とフィールドはスキーマが定義する。
  • ResolverはGraphQLフィールドの引数などからデータソース要求を作り、応答をGraphQLの結果へ変換する。DynamoDB Resolver、Lambda Resolver、Pipeline Resolverなどを要件で使い分ける。
  • AppSync Subscriptionは対応づけたGraphQL Mutationを契機にWebSocketで配信する。データベースをAppSync外から変更しただけでは自動通知されない。
  • AppSyncのオフライン同期にはクライアント側の同期機能や、バージョン管理されたデータソース、Delta Syncなどの構成が必要。Subscriptionとオフライン同期は同じ機能ではない。
  • AppSyncはAPIキー、IAM、Cognito User Pools、OIDC、Lambdaの認証方式を提供する。認証に加え、フィールドやデータごとの認可も設計する。
  • API GatewayはHTTP/REST API、AppSyncはGraphQL APIを中心にしたサービスである。要件に応じて選び分ける。

AWS公式ドキュメント

関連トピック

重要ポイント

  • ▸AWS AppSyncはマネージドなGraphQL APIサービスで、スキーマに沿ったデータ取得・変更とリアルタイム通知を提供する
  • ▸GraphQLのQueryは読み取り、Mutationはデータ変更、SubscriptionはMutationを契機とするリアルタイム配信に使う
  • ▸ResolverはGraphQLフィールドとデータソースをつなぎ、要求をバックエンド操作へ変換して結果をGraphQL応答へ整える
  • ▸DynamoDB、Lambda、Aurora、OpenSearch Service、HTTPエンドポイントなどをデータソースとして組み合わせられる
  • ▸オフライン同期にはクライアント側の仕組みやバージョン管理されたデータソース、Delta Syncなどの設計が必要

このトピックの学習を完了しますか?

完了状態はいつでも切り替えられます