AWS AppSync でのパイプラインリゾルバーの使用 - AWS AppSync GraphQL

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

AWS AppSync でのパイプラインリゾルバーの使用

注記

現在、主に APPSYNC_JS ランタイムとそのドキュメントをサポートしています。こちらにある APPSYNC_JS ランタイムとそのガイドの使用をご検討ください。

AWS AppSync は、GraphQL フィールドをユニットリゾルバー経由で 1 つのデータソースにつなぐシンプルな方法を提供します。ただし、1 つのオペレーションを実行するだけでは不十分な場合があります。パイプラインリゾルバーは、データソースに対してオペレーションを順番に実行する機能を提供します。API で関数を作成し、パイプラインリゾルバーにアタッチします。各関数の実行結果は、実行する関数がなくなるまで、次の結果にパイプされます。パイプラインリゾルバーを使用すると、AWS AppSync で直接、より複雑なワークフローを構築できます。このチュートリアルでは、友人によって投稿された写真を投稿したり表示したりできる、シンプルな写真表示アプリケーションを作成します。

ワンクリックでのセットアップ

設定したすべてのリゾルバーと必要な AWS リソースを使用して AWS AppSync に GraphQL エンドポイントが自動的に設定されるようにする場合は、以下の AWS CloudFormation テンプレートを使用できます。

Blue button labeled "Launch Stack" with an arrow icon indicating an action to start.

このスタックはお客様のアカウントに以下のリソースを作成します。

  • AWS AppSync がお客様のアカウントのリソースにアクセスするための IAM ロール

  • 2 DynamoDB テーブル

  • 1 つの Amazon Cognito ユーザープール

  • 2 つの Amazon Cognito ユーザープールグループ

  • 3 つの Amazon Cognito ユーザープールユーザー

  • 1 AWS AppSync API

AWS CloudFormation スタック作成プロセスの最後に、作成された 3 つの Amazon Cognito ユーザーごとに 1 件の E メールを受信します。各 E メールには、Amazon Cognito ユーザーとして AWS AppSync コンソールにログインするために使用する一時パスワードが含まれています。これらのパスワードを保存し、チュートリアルの残りで使用します。

手動セットアップ

AWS AppSync コンソールからステップバイステップのプロセスを手動で実行する場合は、以下の設定プロセスに従います。

AWS AppSync リソース以外のリソースの設定

API は、2 つの DynamoDB テーブル (写真を保存する pictures テーブルおよびユーザー間の関係を保存する friends テーブル) とやり取りします。API は、認証タイプとして Amazon Cognito ユーザープールを使用するように設定されています。以下の AWS CloudFormation スタックはアカウント内にこれらのリソースを設定します。

Blue button labeled "Launch Stack" with an arrow icon indicating an action to start.

AWS CloudFormation スタック作成プロセスの最後に、作成された 3 つの Amazon Cognito ユーザーごとに 1 件の E メールを受信します。各 E メールには、Amazon Cognito ユーザーとして AWS AppSync コンソールにログインするために使用する一時パスワードが含まれています。これらのパスワードを保存し、チュートリアルの残りで使用します。

GraphQL API の作成

AWS AppSync で GraphQL API を作成するには、以下の手順に従います。

  1. AWS AppSync コンソールを開き、[Build From Scratch (最初から構築)]、[Start (開始)] の順に選択します。

  2. API の名前を AppSyncTutorial-PicturesViewer に設定します。

  3. [Create] (作成) を選択します。

AWS AppSync コンソールによって、API キー認証モードを使用して新しい GraphQL API が作成されます。このコンソールを使用して、残りの GraphQL API をセットアップでき、このチュートリアルの残りの部分でクエリを実行できます。

GraphQL API の設定

先ほど作成した Amazon Cognito ユーザープールを使用して AWS AppSync API を設定する必要があります。

  1. [Settings] (設定) タブを選択します。

  2. [Authorization Type (承認タイプ)] セクションで、Amazon Cognito ユーザープールを選択します。

  3. [ユーザープールの設定] で、AWSリージョンUS-WEST-2を選択します。

  4. AppSyncTutorial-UserPool ユーザープールを選択します。

  5. デフォルトアクションとして拒否を選択します。

  6. [AppId client regex (AppId クライアント正規表現)] フィールドは空白のままにします。

  7. [Save] を選択します。

これで、承認タイプとして Amazon Cognito ユーザープールを使用するように API が設定されました。

DynamoDB テーブル用のデータソースの設定

DynamoDB テーブルを作成した後、コンソールで AWS AppSync GraphQL API に移動し、[データソース] タブを選択します。次は、先ほど作成した DynamoDB テーブルごとに、AWS AppSync でデータソースを作成します。

  1. [Data source (データソース)] タブを選択します。

  2. [New (新規)] を選択して、新しいデータソースを作成します。

  3. データソース名に、PicturesDynamoDBTable を入力します。

  4. データソースのタイプとして [Amazon DynamoDB Table (Amazon DynamoDB テーブル)] を選択します。

  5. リージョンとして [US-WEST-2 (米国西部 (オレゴン))] を選択します。

  6. テーブルのリストから AppSyncTutorial-Pictures DynamoDB テーブルを選択します。

  7. Create or use an existing role (作成または既存のロールの使用)」セクションで [Existing role (既存のロール)] を選択します。

  8. CloudFormation テンプレートから先ほど作成したロールを選択します。ResourceNamePrefix を変更しなかった場合、ロールの名前は AppSyncTutorial-DynamoDBRole になっています。

  9. [Create] (作成) を選択します。

friendsテーブルについても同じプロセスを繰り返します。CloudFormation スタックの作成時のResourceNamePrefixパラメータを変更しなかった場合、DynamoDB テーブルの名前はAppSyncTutorial-Friendsになります。

GraphQL スキーマの作成

データソースが DynamoDB テーブルに接続されたところで、GraphQL スキーマを作成しましょう。AWS AppSync コンソールのスキーマエディタで、スキーマが以下のスキーマと一致することを確認します。

schema { query: Query mutation: Mutation } type Mutation { createPicture(input: CreatePictureInput!): Picture! @aws_auth(cognito_groups: ["Admins"]) createFriendship(id: ID!, target: ID!): Boolean @aws_auth(cognito_groups: ["Admins"]) } type Query { getPicturesByOwner(id: ID!): [Picture] @aws_auth(cognito_groups: ["Admins", "Viewers"]) } type Picture { id: ID! owner: ID! src: String } input CreatePictureInput { owner: ID! src: String! }

スキーマを保存するには、[Save Schema (スキーマの保存)] を選択します。

いくつかのスキーマフィールドには @aws_auth ディレクティブで注釈が付けられています。API のデフォルトのアクション設定は [拒否] に設定されているため、API は @aws_auth ディレクティブ内で指定されているグループのメンバーではないすべてのユーザーを拒否します。API の保護方法の詳細については、「セキュリティ」ページを参照してください。この場合、管理者ユーザーのみが Mutation.createPicture および Mutation.createFriendship フィールドにアクセスできますが、Admins または Viewers グループのメンバーであるユーザーは Query.getPicturesByOwner フィールドにアクセスできます。他のすべてのユーザーはアクセスできません。

リゾルバーの設定

有効な GraphQL スキーマと 2 つのデータソースを用意できたところで、スキーマでリゾルバーを GraphQL フィールドにアタッチできます。API は以下の機能を提供します。

  • Mutation.createPicture フィールドで写真を作成する

  • Mutation.createFriendship フィールドで友人関係を作成する

  • Query.getPicture フィールドで写真を取得する

Mutation.createPicture

AWS AppSync コンソールのスキーマエディタで、createPicture(input: CreatePictureInput!): Picture! のために、右側にある [Attach Resolver (リゾルバーをアタッチ)] を選択します。DynamoDB PicturesDynamoDBTableデータソースを選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。

#set($id = $util.autoId()) { "version" : "2018-05-29", "operation" : "PutItem", "key" : { "id" : $util.dynamodb.toDynamoDBJson($id), "owner": $util.dynamodb.toDynamoDBJson($ctx.args.input.owner) }, "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args.input) }

[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。

#if($ctx.error) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)

写真の作成機能が実行されます。ランダムに生成された UUID を写真の ID として使用し、Cognito のユーザー名を写真の所有者として使用して、Pictures テーブルに写真を保存します。

Mutation.createFriendship

AWS AppSync コンソールのスキーマエディタで、createFriendship(id: ID!, target: ID!): Boolean のために、右側にある [Attach Resolver (リゾルバーをアタッチ)] を選択します。DynamoDB FriendsDynamoDBTableデータソースを選択します。[リクエストマッピングテンプレート] セクションで、以下のテンプレートを追加します。

#set($userToFriendFriendship = { "userId" : "$ctx.args.id", "friendId": "$ctx.args.target" }) #set($friendToUserFriendship = { "userId" : "$ctx.args.target", "friendId": "$ctx.args.id" }) #set($friendsItems = [$util.dynamodb.toMapValues($userToFriendFriendship), $util.dynamodb.toMapValues($friendToUserFriendship)]) { "version" : "2018-05-29", "operation" : "BatchPutItem", "tables" : { ## Replace 'AppSyncTutorial-' default below with the ResourceNamePrefix you provided in the CloudFormation template "AppSyncTutorial-Friends": $util.toJson($friendsItems) } }

重要: BatchPutItem リクエストテンプレートには、DynamoDB テーブルの正確な名前が指定されている必要があります。デフォルトのテーブル名は AppSyncTutorial-Friends です。間違ったテーブル名を使用している場合、指定したロールを AppSync が引き受けようとするとエラーが発生します。

このチュートリアルでは、シンプルにすることを目的に、友人関係のリクエストが承認されたかのように処理を進め、関係のエントリを AppSyncTutorialFriends テーブルに直接保存します。

実際、友人関係は双方向であるため、関係ごとに 2 つの項目を保存します。多対多の関係を表すための Amazon DynamoDB のベストプラクティスの詳細については、「DynamoDB のベストプラクティス」を参照してください。

[レスポンスマッピングテンプレート] セクションで、以下のテンプレートを追加します。

#if($ctx.error) $util.error($ctx.error.message, $ctx.error.type) #end true

注意: リクエストテンプレートに正しいテーブル名が含まれていることを確認してください。デフォルト名は AppSyncTutorial-Friends ですが、CloudFormation の ResourceNamePrefix パラメータを変更した場合、そのテーブル名が異なることがあります。

Query.getPicturesByOwner

友人関係と写真を用意できたところで、ユーザーが自分の友人の写真を表示できるようにする必要があります。この要件を満たすには、まずリクエスト者が所有者と友人であることを確認し、最後に写真のクエリを実行する必要があります。

この機能には 2 つのデータソースオペレーションが必要であるため、2 つの関数を作成します。最初の関数 isFriend は、リクエスト者と所有者が友人であるかどうかを確認します。2 番目の関数 getPicturesByOwner は、所有者 ID を指定してリクエストされた写真を取得します。Query.getPicturesByOwner フィールドで提案されたリゾルバーに対する以下の実行フローを見てみましょう。

  1. Before マッピングテンプレート: コンテキストとフィールドの入力引数を準備します。

  2. isFriend 関数: リクエスト者が写真の所有者かどうかを確認します。そうでない場合は、friends テーブルに対して DynamoDB GetItem オペレーションを実行して、リスクエスト者と所有者が友人であるかどうかを確認します。

  3. GetPicturebyOwner 関数: owner-indexグローバルセカンダリインデックスの DynamoDB クエリ操作を使用して、ピクチャテーブルから画像を取得します。

  4. After マッピングテンプレート: DynamoDB 属性が、想定される GraphQL タイプのフィールドに正しくマッピングされるように、写真の結果をマッピングします。

まず、関数を作成しましょう。

isFriend 関数
  1. [関数] タブをクリックします。

  2. [関数の作成] を選択して、関数を作成します。

  3. データソース名に、FriendsDynamoDBTable を入力します。

  4. 関数名として、「isFriend」と入力します。

  5. リクエストマッピングテンプレートのテキスト領域内に、以下のテンプレートを貼り付けます。

    #set($ownerId = $ctx.prev.result.owner) #set($callerId = $ctx.prev.result.callerId) ## if the owner is the caller, no need to make the check #if($ownerId == $callerId) #return($ctx.prev.result) #end { "version" : "2018-05-29", "operation" : "GetItem", "key" : { "userId" : $util.dynamodb.toDynamoDBJson($callerId), "friendId" : $util.dynamodb.toDynamoDBJson($ownerId) } }
  6. レスポンスマッピングテンプレートのテキスト領域内に、以下のテンプレートを貼り付けます。

    #if($ctx.error) $util.error("Unable to retrieve friend mapping message: ${ctx.error.message}", $ctx.error.type) #end ## if the users aren't friends #if(!$ctx.result) $util.unauthorized() #end $util.toJson($ctx.prev.result)
  7. [関数の作成] を選択します。

結果: isFriend 関数を作成しました。

getPicturesByOwner 関数
  1. [関数] タブをクリックします。

  2. [関数の作成] を選択して、関数を作成します。

  3. データソース名に、PicturesDynamoDBTable を入力します。

  4. 関数名として、「getPicturesByOwner」と入力します。

  5. リクエストマッピングテンプレートのテキスト領域内に、以下のテンプレートを貼り付けます。

    { "version" : "2018-05-29", "operation" : "Query", "query" : { "expression": "#owner = :owner", "expressionNames": { "#owner" : "owner" }, "expressionValues" : { ":owner" : $util.dynamodb.toDynamoDBJson($ctx.prev.result.owner) } }, "index": "owner-index" }
  6. レスポンスマッピングテンプレートのテキスト領域内に、以下のテンプレートを貼り付けます。

    #if($ctx.error) $util.error($ctx.error.message, $ctx.error.type) #end $util.toJson($ctx.result)
  7. [関数の作成] を選択します。

結果: getPicturesByOwner 関数を作成しました。関数が作成されたところで、パイプラインリゾルバーを Query.getPicturesByOwner フィールドにアタッチします。

AWS AppSync コンソールのスキーマエディタで、Query.getPicturesByOwner(id: ID!): [Picture] のために、右側にある [Attach Resolver (リゾルバーをアタッチ)] を選択します。以下のページで、データソースのドロップダウンリストの下に表示される [Convert to pipeline resolver (パイプラインリゾルバーに変換)] リンクを選択します。Before マッピングテンプレートに、以下のものを使用します。

#set($result = { "owner": $ctx.args.id, "callerId": $ctx.identity.username }) $util.toJson($result)

After マッピングテンプレートのセクションに、以下のものを使用します。

#foreach($picture in $ctx.result.items) ## prepend "src://" to picture.src property #set($picture['src'] = "src://${picture['src']}") #end $util.toJson($ctx.result.items)

[Create Resolver (リゾルバー作成)] を選択します。最初のパイプラインリゾルバーが正常にアタッチされました。同じページで、前に作成した 2 つの関数を追加します。関数セクションで、[Add A Function (関数の追加)] を選択してから、最初の関数の名前として [isFriend] を選択または入力します。getPicturesByOwner 関数のものと同じプロセスに従って、2 番目の関数を追加します。isFriend 関数がリストの最初に表示され、続いて getPicturesByOwner 関数が表示されていることを確認します。上下の矢印を使用して、パイプライン内の関数の実行順序に並べ替えることができます。

パイプラインリゾルバーが作成され、関数がアタッチされたところで、新しく作成した GraphQL API をテストしましょう。

GraphQL API をテストする

まず、作成した管理者ユーザーを使用していくつかのミューテーションを実行することで、写真と友人関係を入力する必要があります。AWS AppSync コンソールの左側で、[クエリ] タブを選択します。

createPicture ミューテーション

  1. AWS AppSync コンソールで、[クエリ] タブを選択します。

  2. [Login With User Pools (ユーザープールでログイン)] を選択します。

  3. モーダルで、CloudFormation スタックによって作成された Cognito Sample Client ID (37solo6mmhh7k4v63cqdfgdg5d など) を入力します。

  4. CloudFormation スタックにパラメータとして渡したユーザー名を入力します。デフォルトは nadia です。

  5. E メールに送信されて CloudFormation スタックへのパラメータとして渡した一時パスワード (UserPoolUserEmail など) を使用します。

  6. [ログイン] を選択します。これで、ボタンの名前が Logout nadia に変更されているか、CloudFormation スタックの作成時に選択したユーザー名 (つまり UserPoolUsername) に変更されています。

pictures テーブルに入力するいくつかの createPicture ミューテーションを送信しましょう。コンソール内で以下の GraphQL クエリを実行します。

mutation { createPicture(input:{ owner: "nadia" src: "nadia.jpg" }) { id owner src } }

レスポンスは以下のようになります。

{ "data": { "createPicture": { "id": "c6fedbbe-57ad-4da3-860a-ffe8d039882a", "owner": "nadia", "src": "nadia.jpg" } } }

さらに写真をいくつか追加しましょう。

mutation { createPicture(input:{ owner: "shaggy" src: "shaggy.jpg" }) { id owner src } }
mutation { createPicture(input:{ owner: "rex" src: "rex.jpg" }) { id owner src } }

管理者ユーザーとして nadia を使用して 3 つの写真を追加しました。

createFriendship ミューテーション

友人関係エントリを追加しましょう。コンソールで以下のミューテーションを実行します。

注意: 管理者ユーザーとしてログインしている必要があります (デフォルトの管理者ユーザーは nadia です)。

mutation { createFriendship(id: "nadia", target: "shaggy") }

レスポンスは以下のようになります。

{ "data": { "createFriendship": true } }

nadiashaggy は友人です。rex はだれとも友人ではありません。

getPicturesByOwner クエリ

この手順では、Cognito ユーザープールと共に、このチュートリアルの始めに設定した認証情報を使用して、nadia ユーザーとしてログインします。nadia として、shaggy が所有する写真を取得します。

query { getPicturesByOwner(id: "shaggy") { id owner src } }

nadiashaggy は友人であるため、クエリからは対応する写真が返されます。

{ "data": { "getPicturesByOwner": [ { "id": "05a16fba-cc29-41ee-a8d5-4e791f4f1079", "owner": "shaggy", "src": "src://shaggy.jpg" } ] } }

同様に、nadia が自分の写真を取得しようとした場合も、クエリは成功します。その場合、isFriend GetItem オペレーションを実行しないように、パイプラインリゾルバーは最適化されています。以下のクエリを試します。

query { getPicturesByOwner(id: "nadia") { id owner src } }

API のログ記録を有効にしている場合 ([Settings (設定)] ペインで)、デバッグレベルを [すべて] に設定し、同じクエリをもう一度実行すると、フィールド実行のログが返されます。ログを見ることで、リクエストマッピングテンプレートステージでisFriend関数が早期に返されたかどうか判断することができます。

{ "errors": [], "mappingTemplateType": "Request Mapping", "path": "[getPicturesByOwner]", "resolverArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/types/Query/fields/getPicturesByOwner", "functionArn": "arn:aws:appsync:us-west-2:XXXX:apis/XXXX/functions/o2f42p2jrfdl3dw7s6xub2csdfs", "functionName": "isFriend", "earlyReturnedValue": { "owner": "nadia", "callerId": "nadia" }, "context": { "arguments": { "id": "nadia" }, "prev": { "result": { "owner": "nadia", "callerId": "nadia" } }, "stash": {}, "outErrors": [] }, "fieldInError": false }

earlyReturnedValue キーは、#return ディレクティブによって返されたデータを表します。

最後に、rexViewers Cognito ユーザープールグループのメンバーです。rex はだれとも友人ではないため、shaggy または nadia によって所有されている写真にアクセスすることはできません。コンソールに rex としてログインし、以下のクエリを実行したとします。

query { getPicturesByOwner(id: "nadia") { id owner src } }

以下の未承認エラーが発生します。

{ "data": { "getPicturesByOwner": null }, "errors": [ { "path": [ "getPicturesByOwner" ], "data": null, "errorType": "Unauthorized", "errorInfo": null, "locations": [ { "line": 2, "column": 9, "sourceName": null } ], "message": "Not Authorized to access getPicturesByOwner on type Query" } ] }

パイプラインリゾルバーを使用した複雑な承認が正常に実装されました。