

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

# AWS AppSync でのパイプラインリゾルバーの使用
<a name="tutorial-pipeline-resolvers"></a>

**注記**  
現在、主に APPSYNC\$1JS ランタイムとそのドキュメントをサポートしています。[こちら](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html) で APPSYNC\$1JS ランタイムとそのガイドの使用をご検討ください。

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

## ワンクリックでのセットアップ
<a name="one-click-setup"></a>

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

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-full.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-full.yaml)

このスタックはお客様のアカウントに以下のリソースを作成します。
+ アカウントのリソースにアクセスするための 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 メールには、 AWS AppSync コンソールへの Amazon Cognito ユーザーとしてログインするために使用する一時パスワードが含まれています。これらのパスワードを保存し、チュートリアルの残りで使用します。

## 手動セットアップ
<a name="manual-setup"></a>

 AWS AppSync コンソールを使用してstep-by-stepのプロセスを手動で実行する場合は、以下のセットアッププロセスに従います。

### 非 AWS AppSync リソースのセットアップ
<a name="setting-up-your-non-aws-appsync-resources"></a>

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

[https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-resources-only.yaml](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?templateURL=https://s3.us-west-2.amazonaws.com/awsappsync/resources/pipeline/pipeline-resolvers-resources-only.yaml)

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

### GraphQL API の作成
<a name="creating-your-graphql-api"></a>

 AWS AppSync で GraphQL API を作成するには:

1.  AWS AppSync コンソールを開き、**Build From Scratch** を選択し、**Start** を選択します。

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

1. **[作成]** を選択します。

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

### GraphQL API の設定
<a name="configuring-the-graphql-api"></a>

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

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

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

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

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

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

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

1. **[保存]** を選択します。

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

### DynamoDB テーブル用のデータソースの設定
<a name="configuring-data-sources-for-the-ddb-tables"></a>

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

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

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

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

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

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

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

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

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

1. **[作成]** を選択します。

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

### GraphQL スキーマの作成
<a name="creating-the-graphql-schema"></a>

データソースが 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\$1auth* ディレクティブで注釈が付けられています。API のデフォルトのアクション設定は [*拒否*] に設定されているため、API は *@aws\$1auth* ディレクティブ内で指定されているグループのメンバーではないすべてのユーザーを拒否します。API の保護方法の詳細については、「[セキュリティ](security-authz.md#aws-appsync-security)」ページを参照してください。この場合、管理者ユーザーのみが *Mutation.createPicture* および *Mutation.createFriendship* フィールドにアクセスできま、*Admins* または *Viewers* グループのメンバーのユーザーは *Query.getPicturesByOwner* フィールドにアクセスできます。他のすべてのユーザーはアクセスできません。

### リゾルバーの設定
<a name="configuring-resolvers"></a>

有効な GraphQL スキーマと 2 つのデータソースを用意できたところで、スキーマでリゾルバーを GraphQL フィールドにアタッチできます。API は以下の機能を提供します。
+ *Mutation.createPicture* フィールドで写真を作成する
+ *Mutation.createFriendship* フィールドで友人関係を作成する
+ *Query.getPicture* フィールドで写真を取得する

#### Mutation.createPicture
<a name="mutation-createpicture"></a>

 AWS AppSync コンソールのスキーマエディタから、右側の「 **のリゾルバーをア**タッチする」を選択します`createPicture(input: CreatePictureInput!): Picture!`。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
<a name="mutation-createfriendship"></a>

 AWS AppSync コンソールのスキーマエディタから、右側の「 **のリゾルバーをア**タッチする」を選択します`createFriendship(id: ID!, target: ID!): Boolean`。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 のベストプラクティス](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-adjacency-graphs.html)」を参照してください。

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

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

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

#### Query.getPicturesByOwner
<a name="query-getpicturesbyowner"></a>

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

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

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

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

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

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

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

##### isFriend 関数
<a name="isfriend-function"></a>

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

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

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

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

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

   ```
   #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)
       }
   }
   ```

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

   ```
   #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)
   ```

1. [**関数の作成**] を選択します。

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

##### getPicturesByOwner 関数
<a name="getpicturesbyowner-function"></a>

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

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

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

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

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

   ```
   {
       "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"
   }
   ```

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

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

1. [**関数の作成**] を選択します。

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

 AWS AppSync コンソールのスキーマエディタから、右側の「 **のリゾルバーをア**タッチする」を選択します`Query.getPicturesByOwner(id: ID!): [Picture]`。以下のページで、データソースのドロップダウンリストの下に表示される [**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 をテストする
<a name="testing-your-graphql-api"></a>

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

### createPicture ミューテーション
<a name="createpicture-mutation"></a>

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

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

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

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

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

1. [**ログイン**] を選択します。これで、ボタンの名前が **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 ミューテーション
<a name="createfriendship-mutation"></a>

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

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

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

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

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

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

### getPicturesByOwner クエリ
<a name="getpicturesbyowner-query"></a>

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

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

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

```
{
  "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* キーは、*\$1return* ディレクティブによって返されたデータを表します。

最後に、**rex** は **Viewers** 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"
    }
  ]
}
```

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