

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

# AWS AppSync での DynamoDB バッチオペレーションの使用
<a name="tutorial-dynamodb-batch-js"></a>

AWS AppSync は、1 つのリージョン内の 1 つ以上のテーブルで Amazon DynamoDB バッチオペレーションの使用をサポートしています。サポートされている処理は、`BatchGetItem`、`BatchPutItem`、および `BatchDeleteItem` です。 AWS AppSync でこれらの機能を使用すると、次のようなタスクを実行できます。
+ 単一クエリでキーのリストを渡し、テーブルからの結果を返す
+ 単一クエリで 1 つ以上のテーブルからレコードを読み取る
+ 1 つ以上のテーブルに一連のレコードを書き込む
+ 関連のある複数のテーブルのレコードを状況に応じて書き込む、または削除する

 AWS AppSync のバッチオペレーションには、バッチ処理されていないオペレーションと 2 つの主な違いがあります。
+ データソースのロールには、リゾルバーがアクセスするすべてのテーブルについてアクセス許可が必要です。
+ リゾルバーのテーブル仕様はリクエストオブジェクトに含まれます。

## 1 つのテーブルのバッチ処理
<a name="single-table-batch-js"></a>

**警告**  
`BatchPutItem` および `BatchDeleteItem` は、競合の検出と解決とともに使用する場合はサポートされていません。これらの設定は、起こり得るエラーを防ぐために無効にする必要があります。

はじめに、新しい GraphQL API を作成しましょう。 AWS AppSync コンソールで、API、**GraphQL APIs**、**および設計を最初から****作成**を選択します。API に名前を付け`BatchTutorial API`、「**次へ**」を選択し、「**GraphQL リソースを指定**」ステップで「**GraphQL リソースを後で作成**」を選択して「**次へ**」をクリックします。詳細を確認して API を作成します。**スキーマ**ページに移動して以下のスキーマを張り付けます。クエリには ID のリストを渡すことに注意してください：

```
type Post {
    id: ID!
    title: String
}

input PostInput {
    id: ID!
    title: String
}

type Query {
    batchGet(ids: [ID]): [Post]
}

type Mutation {
    batchAdd(posts: [PostInput]): [Post]
    batchDelete(ids: [ID]): [Post]
}
```

スキーマを保存した後、ページ上部の **[リソースを作成]** を選択します。**[既存のタイプを使用]** を選択し、`Post` タイプを選択します。`Posts` テーブルに名前を付けます。**プライマリキー**が `id` に設定されていることを確認し、「**GraphQL を自動生成**」を選択解除し (独自のコードを指定します)、「**作成**」を選択します。はじめに、 AWS AppSync で新しい DynamoDB テーブルを作成し、そのテーブルに適切なロールで接続されたデータソースを作成します。ただし、ロールに追加する必要のあるアクセス許可がまだいくつかあります。**[データソース]** ページに移動し、新しいデータソースを選択します。**[既存のロールを選択]** で、テーブルのロールが自動的に作成されたことがわかります。ロール (`appsync-ds-ddb-aaabbbcccddd-Posts` のような表示になっているはずです) をメモし、IAM コンソール ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)) に移動します。IAM コンソールで **[ロール]** を選択し、テーブルからロールを選択します。自身のロールの **[アクセス許可ポリシー]** で、ポリシーの横にある [`+`] をクリックします (ロール名と似た名前になっているはずです)。ポリシーが表示されたら、折りたたみ可能なウィンドウの上部にある **[編集]** を選択します。ポリシーにバッチ許可、具体的には`dynamodb:BatchGetItem` と `dynamodb:BatchWriteItem` を追加する必要があります。次のように表示されます。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:PutItem",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:UpdateItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:BatchGetItem"
            ],
            "Resource": [
                "arn:aws:dynamodb:us-east-1:111122223333:table/locationReadings",
                "arn:aws:dynamodb:us-east-1:111122223333:table/locationReadings/*",
                "arn:aws:dynamodb:us-east-1:111122223333:table/temperatureReadings",
                "arn:aws:dynamodb:us-east-1:111122223333:table/temperatureReadings/*"
            ]
        }
    ]
}
```

------

**[次へ]** を選択し、**[変更を保存]** を選択します。これで、ポリシーでバッチ処理が許可されるはずです。

 AWS AppSync コンソールに戻り、**スキーマ**ページに移動し、`Mutation.batchAdd`フィールドの横にある**ア**タッチを選択します。`Posts` テーブルをデータソースとして使用してリゾルバーを作成します。コードエディターで、ハンドラーを以下のスニペットに置き換えます。これは自動的に GraphQL の `input PostInput` 型に各項目を渡し、マップを作成します。このマップは `BatchPutItem` オペレーションに必要になります。

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

export function request(ctx) {
  return {
    operation: "BatchPutItem",
    tables: {
      Posts: ctx.args.posts.map((post) => util.dynamodb.toMapValues(post)),
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

 AWS AppSync コンソールの**クエリ**ページに移動し、次の`batchAdd`ミューテーションを実行します。

```
mutation add {
    batchAdd(posts:[{
            id: 1 title: "Running in the Park"},{
            id: 2 title: "Playing fetch"
        }]){
            id
            title
    }
}
```

結果が画面に表示されるはずです。これは、DynamoDB コンソールを確認して、`Posts` テーブルに書き込まれた値をスキャンすることで検証できます。

次に、`Posts` テーブルをデータソースとして使用する `Query.batchGet` フィールドに対してリゾルバーをアタッチするプロセスを繰り返します。ハンドラーを以下のコードに置き換えます。これは自動的に GraphQL の `ids:[]` 型に各項目を渡し、マップを作成します。このマップは `BatchGetItem` の処理に必要になります。

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

export function request(ctx) {
  return {
    operation: "BatchGetItem",
    tables: {
      Posts: {
        keys: ctx.args.ids.map((id) => util.dynamodb.toMapValues({ id })),
        consistentRead: true,
      },
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

次に、 AWS AppSync コンソールの**クエリ**ページに戻り、次の`batchGet`クエリを実行します。

```
query get {
    batchGet(ids:[1,2,3]){
        id
        title
    }
}
```

これは、以前に追加した 2 つの `id` 値の結果を返します。値が `null` の `id` に対して `3` 値が返っていることに注意してください。これは、その値に対応するレコードが `Posts` テーブルにまだないためです。また、 AWS AppSync はクエリに渡されたキーと同じ順序で結果を返します。これは、 AWS AppSync がユーザーに代わって実行する追加機能です。したがって、`batchGet(ids:[1,3,2])` に変更すると、順番が代わります。どの `id` で `null` 値が返されたのかもこれで分かります。

最後に、`Posts` テーブルをデータソースとして使用して、`Mutation.batchDelete` フィールドにもう 1 つリゾルバーをアタッチします。ハンドラーを以下のコードに置き換えます。これは自動的に GraphQL の `ids:[]` 型に各項目を渡し、マップを作成します。このマップは `BatchGetItem` の処理に必要になります。

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

export function request(ctx) {
  return {
    operation: "BatchDeleteItem",
    tables: {
      Posts: ctx.args.ids.map((id) => util.dynamodb.toMapValues({ id })),
    },
  };
}

export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type);
  }
  return ctx.result.data.Posts;
}
```

次に、 AWS AppSync コンソールの**クエリ**ページに戻り、次の`batchDelete`ミューテーションを実行します。

```
mutation delete {
    batchDelete(ids:[1,2]){ id }
}
```

`id` が `1` と `2` のレコードが削除されます。以前に使用した `batchGet()` クエリを再実行すると、これらは `null` を返します。

## 複数テーブルのバッチ処理
<a name="multi-table-batch-js"></a>

**警告**  
`BatchPutItem` および `BatchDeleteItem` は、競合の検出と解決とともに使用する場合はサポートされていません。これらの設定は、起こり得るエラーを防ぐために無効にする必要があります。

AWS AppSync では、テーブル間でバッチオペレーションを実行することもできます。より複雑なアプリケーションを作成しましょう。Pet Health アプリを作成するとします。これは、センサーによりペットの場所と体温を報告します。センサーは電池により動作し、数分ごとにネットワークへの接続を試みます。センサーが接続を確立すると、その読み取り値が AWS AppSync API に送信されます。その後、データの分析がトリガーされ、ペットの飼い主にダッシュボードが表示されます。センサーとバックエンドのデータストア間のやり取りの作成について考えてみましょう。

 AWS AppSync コンソールで、API、**GraphQL APIs**、**および設計を最初から****作成**を選択します。API に名前を付け`MultiBatchTutorial API`、「**次へ**」を選択し、「**GraphQL リソースを指定**」ステップで「**GraphQL リソースを後で作成**」を選択して「**次へ**」をクリックします。詳細を確認して API を作成します。「**スキーマ**」ページに移動し、次のスキーマを貼り付けて保存します。

```
type Mutation {
    # Register a batch of readings
    recordReadings(tempReadings: [TemperatureReadingInput], locReadings: [LocationReadingInput]): RecordResult
    # Delete a batch of readings
    deleteReadings(tempReadings: [TemperatureReadingInput], locReadings: [LocationReadingInput]): RecordResult
}

type Query {
    # Retrieve all possible readings recorded by a sensor at a specific time
    getReadings(sensorId: ID!, timestamp: String!): [SensorReading]
}

type RecordResult {
    temperatureReadings: [TemperatureReading]
    locationReadings: [LocationReading]
}

interface SensorReading {
    sensorId: ID!
    timestamp: String!
}

# Sensor reading representing the sensor temperature (in Fahrenheit)
type TemperatureReading implements SensorReading {
    sensorId: ID!
    timestamp: String!
    value: Float
}

# Sensor reading representing the sensor location (lat,long)
type LocationReading implements SensorReading {
    sensorId: ID!
    timestamp: String!
    lat: Float
    long: Float
}

input TemperatureReadingInput {
    sensorId: ID!
    timestamp: String
    value: Float
}

input LocationReadingInput {
    sensorId: ID!
    timestamp: String
    lat: Float
    long: Float
}
```

次の 2 つの DynamoDB テーブルを作成する必要があります。
+ `locationReadings` はセンサー位置の読み取り値を格納します。
+ `temperatureReadings` はセンサーの温度測定値を保存します。

両方のテーブルで同じプライマリキー構造体を共有します。`sensorId (String)` はパーティションキーで、`timestamp (String)` がソートキーです。

ページの上部で、**[リソースの作成]** を選択します。**[既存のタイプを使用]** を選択し、`locationReadings` タイプを選択します。`locationReadings` テーブルに名前を付けてください。**プライマリキー** が `sensorId` に、ソートキーが `timestamp` に設定されていることを確認します。**[GraphQL を自動的に生成する]** (独自のコードを指定します) を選択解除し、**[作成]** を選択します。`temperatureReadings` をタイプとテーブル名として使用し,`temperatureReadings` に対してこのプロセスを繰り返して、ます。上記と同じキーを使用してください。

新しいテーブルには、自動的に生成されたロールが含まれます。これらのロールには、まだ追加する必要のあるアクセス許可がいくつかあります。**データソース**ページに移動し、`locationReadings` を選択します。**[既存のロールを選択]** に、そのロールが表示されます。ロール (`appsync-ds-ddb-aaabbbcccddd-locationReadings` のような表示になっているはずです) をメモし、IAM コンソール ([https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)) に移動します。IAM コンソールで **[ロール]** を選択し、テーブルからロールを選択します。自身のロールの **[アクセス許可ポリシー]** で、ポリシーの横にある [`+`] をクリックします (ロール名と似た名前になっているはずです)。ポリシーが表示されたら、折りたたみ可能なウィンドウの上部にある **[編集]** を選択します。このポリシーにアクセス許可を追加する必要があります。次のように表示されます。

**[次へ]** を選択し、**[変更を保存]** を選択します。上記と同じポリシースニペットを使用して、`temperatureReadings` データソースに対してこのプロセスを繰り返します。

### BatchPutItem - センサーの読み取り値の記録
<a name="batchputitem-recording-sensor-readings-js"></a>

センサーは、インターネットに接続後、読み取り値を送信できる必要があります。GraphQL の `Mutation.recordReadings` フィールドは、このために使用される API です。このフィールドにリゾルバーを追加する必要があります。

 AWS AppSync コンソールの**スキーマ**ページで、 `Mutation.recordReadings`フィールドの横にある**ア**タッチを選択します。次の画面で、`locationReadings` テーブルをデータソースとして使用してリゾルバーを作成します。

リゾルバーを作成したら、エディタでハンドラーを次のコードに置き換えます。この`BatchPutItem`操作により、複数のテーブルを指定できます。

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { locReadings, tempReadings } = ctx.args
	const locationReadings = locReadings.map((loc) => util.dynamodb.toMapValues(loc))
	const temperatureReadings = tempReadings.map((tmp) => util.dynamodb.toMapValues(tmp))

	return {
		operation: 'BatchPutItem',
		tables: {
			locationReadings,
			temperatureReadings,
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	return ctx.result.data
}
```

バッチ処理では、呼び出しからエラーと結果の両方が返る可能性があります。その場合は、任意に追加のエラー処理を実行できます。

**注記**  
`utils.appendError()` の使用法は `util.error()` と同様ですが、リクエストまたはレスポンスハンドラーの評価を中断しないという大きな違いがあります。代わりに、エラーが発生したことをフィールドで通知します。ただし、テンプレートを評価して、データを引き続き呼び出し元に返すことも可能です。アプリケーションで部分的な結果を返す必要がある場合は、`utils.appendError()` を使用することを推奨します。

リゾルバーを保存し、 AWS AppSync コンソールの**クエリ**ページに移動します。これで、センサーの読み取り値をいくつか送信できます。

次のミューテーションを実行します。

```
mutation sendReadings {
  recordReadings(
    tempReadings: [
      {sensorId: 1, value: 85.5, timestamp: "2018-02-01T17:21:05.000+08:00"},
      {sensorId: 1, value: 85.7, timestamp: "2018-02-01T17:21:06.000+08:00"},
      {sensorId: 1, value: 85.8, timestamp: "2018-02-01T17:21:07.000+08:00"},
      {sensorId: 1, value: 84.2, timestamp: "2018-02-01T17:21:08.000+08:00"},
      {sensorId: 1, value: 81.5, timestamp: "2018-02-01T17:21:09.000+08:00"}
    ]
    locReadings: [
      {sensorId: 1, lat: 47.615063, long: -122.333551, timestamp: "2018-02-01T17:21:05.000+08:00"},
      {sensorId: 1, lat: 47.615163, long: -122.333552, timestamp: "2018-02-01T17:21:06.000+08:00"},
      {sensorId: 1, lat: 47.615263, long: -122.333553, timestamp: "2018-02-01T17:21:07.000+08:00"},
      {sensorId: 1, lat: 47.615363, long: -122.333554, timestamp: "2018-02-01T17:21:08.000+08:00"},
      {sensorId: 1, lat: 47.615463, long: -122.333555, timestamp: "2018-02-01T17:21:09.000+08:00"}
    ]) {
    locationReadings {
      sensorId
      timestamp
      lat
      long
    }
    temperatureReadings {
      sensorId
      timestamp
      value
    }
  }
}
```

1 つのミューテーションで 10 個のセンサー読み取り値を送信しました。これらは 2 つのテーブルに分けられています。DynamoDB コンソールを使用して、`locationReadings` テーブルと `temperatureReadings` テーブルの両方にデータが表示されることを確認します。

### BatchDeleteItem - センサーの読み取り値の削除
<a name="batchdeleteitem-deleting-sensor-readings-js"></a>

同様に、センサーの読み取り値を一括して削除する必要もあります。これを行うために、`Mutation.deleteReadings` GraphQL フィールドを使用してみましょう。 AWS AppSync コンソールの**スキーマ**ページで、 `Mutation.deleteReadings`フィールドの横にある**ア**タッチを選択します。次の画面で、`locationReadings` テーブルをデータソースとして使用してリゾルバーを作成します。

リゾルバーを作成したら、コードエディターのハンドラーを以下のスニペットに置き換えます。このリゾルバーでは、提供された入力から `sensorId` と `timestamp` を抽出する補助関数マッパーを使用します。

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const { locReadings, tempReadings } = ctx.args
	const mapper = ({ sensorId, timestamp }) => util.dynamodb.toMapValues({ sensorId, timestamp })

	return {
		operation: 'BatchDeleteItem',
		tables: {
			locationReadings: locReadings.map(mapper),
			temperatureReadings: tempReadings.map(mapper),
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	return ctx.result.data
}
```

リゾルバーを保存し、 AWS AppSync コンソールの**クエリ**ページに移動します。では、いくつかのセンサーの読み取り値を削除しましょう\$1

次のミューテーションを実行します。

```
mutation deleteReadings {
  # Let's delete the first two readings we recorded
  deleteReadings(
    tempReadings: [{sensorId: 1, timestamp: "2018-02-01T17:21:05.000+08:00"}]
    locReadings: [{sensorId: 1, timestamp: "2018-02-01T17:21:05.000+08:00"}]) {
    locationReadings {
      sensorId
      timestamp
      lat
      long
    }
    temperatureReadings {
      sensorId
      timestamp
      value
    }
  }
}
```

**注記**  
`DeleteItem` オペレーションとは対照的に、完全に削除された項目はレスポンスで返されません。渡されたキーのみが返されます。詳細については、DynamoDB の [JavaScript リゾルバー関数リファレンスの BatchDeleteItem](https://docs.aws.amazon.com/appsync/latest/devguide/js-resolver-reference-dynamodb.html#js-aws-appsync-resolver-reference-dynamodb-batch-delete-item) を参照してください。

DynamoDB コンソールを通じて `locationReadings` および `temperatureReadings` テーブルから削除されたこれら 2 つの読み取り値を検証します。

### BatchGetItem - 読み取り値の取得
<a name="batchgetitem-retrieve-readings-js"></a>

アプリのもう 1 つの一般的な処理として、特定の時刻にセンサーの読み取り値を取得します。スキーマの `Query.getReadings` GraphQL フィールドにリゾルバーをアタッチしましょう。 AWS AppSync コンソールの**スキーマ**ページで、 `Query.getReadings`フィールドの横にある**ア**タッチを選択します。次の画面で、`locationReadings` テーブルをデータソースとして使用してリゾルバーを作成します。

以下のコードを使用します。

```
import { util } from '@aws-appsync/utils'

export function request(ctx) {
	const keys = [util.dynamodb.toMapValues(ctx.args)]
	const consistentRead = true
	return {
		operation: 'BatchGetItem',
		tables: {
			locationReadings: { keys, consistentRead },
			temperatureReadings: { keys, consistentRead },
		},
	}
}

export function response(ctx) {
	if (ctx.error) {
		util.appendError(ctx.error.message, ctx.error.type)
	}
	const { locationReadings: locs, temperatureReadings: temps } = ctx.result.data

	return [
		...locs.map((l) => ({ ...l, __typename: 'LocationReading' })),
		...temps.map((t) => ({ ...t, __typename: 'TemperatureReading' })),
	]
}
```

リゾルバーを保存し、 AWS AppSync コンソールの**クエリ**ページに移動します。では、センサーの読み取り値を取得しましょう\$1

次のクエリを実行します。

```
query getReadingsForSensorAndTime {
  # Let's retrieve the very first two readings
  getReadings(sensorId: 1, timestamp: "2018-02-01T17:21:06.000+08:00") {
    sensorId
    timestamp
    ...on TemperatureReading {
      value
    }
    ...on LocationReading {
      lat
      long
    }
  }
}
```

以上で、 AWS AppSync を使用した DynamoDB のバッチ処理のデモンストレーションが完了しました。

## エラー処理
<a name="error-handling-js"></a>

In AWS AppSync では、データソースオペレーションが部分的な結果を返すことがあります。部分的な結果という用語は、処理の出力がいくつかのデータと 1 つのエラーで構成されていることを表す場合に使用します。エラー処理は本質的にアプリケーション固有であるため、 AWS AppSync はレスポンスハンドラーでエラーを処理する機会を提供します。リゾルバーの呼び出しエラーがある場合、これは context から `ctx.error` として読み出せます。呼び出しエラーには必ずメッセージと型が含まれています。これらは `ctx.error.message` と `ctx.error.type` というプロパティとしてアクセスできます。レスポンスハンドラーでは、部分的な結果を次の 3 つの方法で処理できます。

1. データを返すだけで、呼び出しエラーは通知しない

1. ハンドラーの評価を停止することでエラーを発生させる (`util.error(...)` を使用)。データは返さない。

1. エラーを付加し (`util.appendError(...)` を使用)、データも返す

DynamoDB のバッチ処理を使用して、上記の 3 つの方法をそれぞれ試してみましょう\$1

### DynamoDB のバッチ処理
<a name="dynamodb-batch-operations-js"></a>

DynamoDB のバッチ処理を使用すると、バッチを部分的に完了させることができます。つまり、リクエストされた項目またはキーの一部を未処理のままにすることができます。 AWS AppSync がバッチを完了できない場合、未処理の項目と呼び出しエラーがコンテキストに設定されます。

このチュートリアルの以前のセクションの `Query.getReadings` 処理で使用した `BatchGetItem` フィールドの設定を使用してエラー処理を実装します。ここでは、`Query.getReadings` フィールドの実行中に、`temperatureReadings` DynamoDB テーブルがプロビジョニングされたスループットを使い果たしたとします。DynamoDB は、 AWS AppSync がバッチ内の残りの要素を処理する 2 回目の試行`ProvisionedThroughputExceededException`中に を発生させました。

次の JSON は、DynamoDB のバッチ処理の呼び出し後、レスポンスハンドラーの呼び出し前にシリアル化された context を表しています。

```
{
  "arguments": {
    "sensorId": "1",
    "timestamp": "2018-02-01T17:21:05.000+08:00"
  },
  "source": null,
  "result": {
    "data": {
      "temperatureReadings": [
        null
      ],
      "locationReadings": [
        {
          "lat": 47.615063,
          "long": -122.333551,
          "sensorId": "1",
          "timestamp": "2018-02-01T17:21:05.000+08:00"
        }
      ]
    },
    "unprocessedKeys": {
      "temperatureReadings": [
        {
          "sensorId": "1",
          "timestamp": "2018-02-01T17:21:05.000+08:00"
        }
      ],
      "locationReadings": []
    }
  },
  "error": {
    "type": "DynamoDB:ProvisionedThroughputExceededException",
    "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
  },
  "outErrors": []
}
```

context に関する注意事項がいくつかあります。
+ 呼び出しエラーは `ctx.error` by AWS AppSync のコンテキストで設定され、エラータイプは に設定されています`DynamoDB:ProvisionedThroughputExceededException`。
+ エラーが存在していても、結果はテーブルごとに `ctx.result.data` にマッピングされます。
+ 未処理のキーは `ctx.result.data.unprocessedKeys` でアクセス可能です。ここでは、テーブルスループットが不十分なため、 AWS AppSync はキー (sensorId:1、timestamp:2018-02-01T17:21:05.000\$108:00) を持つ項目を取得できませんでした。

**注記**  
`BatchPutItem` の場合、これは `ctx.result.data.unprocessedItems` です。`BatchDeleteItem` の場合、これは `ctx.result.data.unprocessedKeys` です。

3 つの異なる方法でこのエラーを処理しましょう。

#### 1. 呼び出しエラーを通知しない
<a name="swallowing-the-invocation-error-js"></a>

呼び出しエラーを処理せずにデータを返して、エラーを実質的に通知しません。指定した GraphQL フィールドの結果は常に成功とします。

記述するコードは通常どおりであり、結果のデータのみにフォーカスします。

**レスポンスハンドラー**

```
export function response(ctx) {
  return ctx.result.data
}
```

**GraphQL レスポンス**

```
{
  "data": {
    "getReadings": [
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "lat": 47.615063,
        "long": -122.333551
      },
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "value": 85.5
      }
    ]
  }
}
```

データのみが有効なため、エラーレスポンスにエラーは追加されません。

#### 2. エラーを発生させて、レスポンスハンドラーの実行を中止する
<a name="raising-an-error-to-abort-the-response-execution-js"></a>

クライアントから見て、部分的な障害を完全な障害として扱う必要がある場合、レスポンスハンドラーの実行を中断することでデータの返送を抑制することができます。この動作には、`util.error(...)` ユーティリティメソッドを使用するのが最適です。

**レスポンスハンドラーコード**

```
export function response(ctx) {
  if (ctx.error) {
    util.error(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return ctx.result.data;
}
```

**GraphQL レスポンス**

```
{
  "data": {
    "getReadings": null
  },
  "errors": [
    {
      "path": [
        "getReadings"
      ],
      "data": null,
      "errorType": "DynamoDB:ProvisionedThroughputExceededException",
      "errorInfo": {
        "temperatureReadings": [
          {
            "sensorId": "1",
            "timestamp": "2018-02-01T17:21:05.000+08:00"
          }
        ],
        "locationReadings": []
      },
      "locations": [
        {
          "line": 58,
          "column": 3
        }
      ],
      "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
    }
  ]
}
```

一部の結果が DynamoDB のバッチ処理から返されてもエラーが発生するように選択して、`getReadings` GraphQL フィールドが null になり、かつ GraphQL レスポンスの *errors* ブロックにエラーが追加されるようにします。

#### 3. エラーを付加してデータとエラーの両方を返す
<a name="appending-an-error-to-return-both-data-and-errors-js"></a>

場合によっては、より優れたユーザーエクスペリエンスを提供するために、アプリケーションから部分的な結果を返すとともに、クライアントに未処理の項目を通知することができます。クライアントでは、再試行を実装することも、エラーを変換してエンドユーザーに通知することもできます。`util.appendError(...)` はこの動作を可能とするユーティリティメソッドであり、アプリケーションの設計者は、レスポンスハンドラーの評価を妨げることなく、context にエラーを付加できます。レスポンスハンドラーを評価した後、 AWS AppSync はコンテキストエラーを GraphQL レスポンスのエラーブロックに追加して処理します。

**レスポンスハンドラーコード**

```
export function response(ctx) {
  if (ctx.error) {
    util.appendError(ctx.error.message, ctx.error.type, null, ctx.result.data.unprocessedKeys);
  }
  return ctx.result.data;
}
```

ここでは、GraphQL レスポンスのエラーブロック内の呼び出しエラーと `unprocessedKeys` 要素の両方が転送されています。以下のレスポンスで分かるとおり、`getReadings` フィールドもまた `locationReadings` テーブルから部分的なデータを返します。

**GraphQL レスポンス**

```
{
  "data": {
    "getReadings": [
      null,
      {
        "sensorId": "1",
        "timestamp": "2018-02-01T17:21:05.000+08:00",
        "value": 85.5
      }
    ]
  },
  "errors": [
    {
      "path": [
        "getReadings"
      ],
      "data": null,
      "errorType": "DynamoDB:ProvisionedThroughputExceededException",
      "errorInfo": {
        "temperatureReadings": [
          {
            "sensorId": "1",
            "timestamp": "2018-02-01T17:21:05.000+08:00"
          }
        ],
        "locationReadings": []
      },
      "locations": [
        {
          "line": 58,
          "column": 3
        }
      ],
      "message": "You exceeded your maximum allowed provisioned throughput for a table or for one or more global secondary indexes. (...)"
    }
  ]
}
```