チュートリアル :DynamoDB Batch リゾルバー - AWS AppSync

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

チュートリアル :DynamoDB Batch リゾルバー

注記

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

AWS AppSyncは、単一リージョン内での複数のテーブルを対象とした Amazon DynamoDB バッチ処理の使用をサポートします。サポートされている処理は、BatchGetItemBatchPutItem、および BatchDeleteItem です。AWS AppSync でこれらの機能を使用すると、次のようなタスクを実行できます。

  • 単一クエリでキーのリストを渡し、テーブルからの結果を返す

  • 単一クエリで 1 つ以上のテーブルからレコードを読み取る

  • 1 つ以上のテーブルに一連のレコードを書き込む

  • 関連のある複数のテーブルのレコードを状況に応じて書き込みまたは削除

AWS AppSync で DynamoDB によるバッチ処理を使用するのは高度な手法であり、バックエンド処理とテーブル構造についてより多くの知識と考慮が必要になります。さらに、AWS AppSync でのバッチ処理には、非バッチ処理と比べて主に 2 つの相違点があります。

  • データソースのロールには、リゾルバーがアクセスするすべてのテーブルについてアクセス許可が必要です。

  • リゾルバーのテーブル仕様はマッピングテンプレートに含まれます。

許可

他のリゾルバーと同様に、AWS AppSync にデータソースを作成し、ロールを作成または既存のロールを使用する必要があります。バッチ処理では DynamoDB テーブルごとに異なるアクセス許可が必要であるため、読み取りまたは書き込みアクションのために設定されたアクセス許可がロールに必要です。

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:BatchGetItem", "dynamodb:BatchWriteItem" ], "Effect": "Allow", "Resource": [ "arn:aws:dynamodb:region:account:table/TABLENAME", "arn:aws:dynamodb:region:account:table/TABLENAME/*" ] } ] }

注意 : ロールは AWS AppSync のデータソースに関連付けられ、フィールドのリゾルバーはデータソースに対して呼び出されます。DynamoDB に対して取得するよう設定されたデータソースは、設定を単純にするために 1 つのテーブルのみ指定されます。したがって、単一のリゾルバーで複数のテーブルに対してバッチ処理を実行する場合、これはより高度なタスクであり、リゾルバーが利用するすべてのテーブルへのアクセスをデータソースのロールに許可する必要があります。これは、上記の IAM ポリシーの Resource フィールドで行われます。テーブルに対してバッチを呼び出すためのテーブル設定は、リゾルバーのテンプレートで行います。これについては以下で説明します。

データソース

分かりやすくするために、このチュートリアルではすべてのリゾルバーに同じデータソースを使用します。[Data sources (データソース)] タブで、新しい DynamoDB データソースを作成し、BatchTutorialという名前を付けます。テーブル名はバッチ処理のリクエストマッピングテンプレートで指定するので、何でも構いません。ここでは、テーブル名に empty を使用しています。

このチュートリアルでは、次のインラインポリシーを含むロールが使用できます。

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:BatchGetItem", "dynamodb:BatchWriteItem" ], "Effect": "Allow", "Resource": [ "arn:aws:dynamodb:region:account:table/Posts", "arn:aws:dynamodb:region:account:table/Posts/*", "arn:aws:dynamodb:region:account:table/locationReadings", "arn:aws:dynamodb:region:account:table/locationReadings/*", "arn:aws:dynamodb:region:account:table/temperatureReadings", "arn:aws:dynamodb:region:account:table/temperatureReadings/*" ] } ] }

1 つのテーブルのバッチ処理

以下の例では、Posts という名前の単一のテーブルがあり、バッチ処理を使用してこれに項目を追加または削除するとします。次のスキーマを使用し、クエリには何もせずに 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] } schema { query: Query mutation: Mutation }

次のリクエストマッピングテンプレートを使用して batchAdd() フィールドにリゾルバーをアタッチします。これは自動的に GraphQL の input PostInput 型に各項目を渡し、マップを作成します。このマップは BatchPutItem の処理に必要になります。

#set($postsdata = []) #foreach($item in ${ctx.args.posts}) $util.qr($postsdata.add($util.dynamodb.toMapValues($item))) #end { "version" : "2018-05-29", "operation" : "BatchPutItem", "tables" : { "Posts": $utils.toJson($postsdata) } }

このケースでは、レスポンスマッピングテンプレートは単純に実行されますが、次のようにテーブル名が ..data.Posts として context オブジェクトに追加されています。

$util.toJson($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テーブルに書き込まれたことを個別に確認できます。

次に、以下のリクエストマッピングテーブルを使用して batchGet() フィールドにリゾルバ―をアタッチします。これは自動的に GraphQL の ids:[] 型に各項目を渡し、マップを作成します。このマップは BatchGetItem の処理に必要になります。

#set($ids = []) #foreach($id in ${ctx.args.ids}) #set($map = {}) $util.qr($map.put("id", $util.dynamodb.toString($id))) $util.qr($ids.add($map)) #end { "version" : "2018-05-29", "operation" : "BatchGetItem", "tables" : { "Posts": { "keys": $util.toJson($ids), "consistentRead": true, "projection" : { "expression" : "#id, title", "expressionNames" : { "#id" : "id"} } } } }

再度、レスポンスマッピングテンプレートが単純に実行されますが、ここでもテーブル名が ..data.Posts として context オブジェクトに追加されます。

$util.toJson($ctx.result.data.Posts)

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

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

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

最後に、以下のリクエストマッピングテンプレートを使用して batchDelete() フィールドにリゾルバーをアタッチします。これは自動的に GraphQL の ids:[] 型に各項目を渡し、マップを作成します。このマップは BatchGetItem の処理に必要になります。

#set($ids = []) #foreach($id in ${ctx.args.ids}) #set($map = {}) $util.qr($map.put("id", $util.dynamodb.toString($id))) $util.qr($ids.add($map)) #end { "version" : "2018-05-29", "operation" : "BatchDeleteItem", "tables" : { "Posts": $util.toJson($ids) } }

再度、レスポンスマッピングテンプレートが単純に実行されますが、ここでもテーブル名が ..data.Posts として context オブジェクトに追加されます。

$util.toJson($ctx.result.data.Posts)

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

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

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

複数テーブルのバッチ処理

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

前提条件として、まず 2 つの DynamoDB テーブルを作成します。locationReadings はセンサーの温度の読み取り値を格納し、temperatureReadings はセンサーの位置の読み取り値を格納します。両方のテーブルで同じプライマリキー構造体を共有します。sensorId (String) はパーティションキーであり、timestamp (String) がソートキーです。

以下の GraphQL スキーマを使用しましょう。

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 }

BatchPutItem - センサーの読み取り値の記録

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

[Mutation.recordReadings] フィールドの横にある [アタッチ] を選択します。次の画面で、チュートリアルの最初で作成したのと同じ BatchTutorial データソースを選択します。

次のリクエストマッピングテンプレートを追加しましょう。

リクエストマッピングテンプレート

## Convert tempReadings arguments to DynamoDB objects #set($tempReadings = []) #foreach($reading in ${ctx.args.tempReadings}) $util.qr($tempReadings.add($util.dynamodb.toMapValues($reading))) #end ## Convert locReadings arguments to DynamoDB objects #set($locReadings = []) #foreach($reading in ${ctx.args.locReadings}) $util.qr($locReadings.add($util.dynamodb.toMapValues($reading))) #end { "version" : "2018-05-29", "operation" : "BatchPutItem", "tables" : { "locationReadings": $utils.toJson($locReadings), "temperatureReadings": $utils.toJson($tempReadings) } }

ご覧のように、BatchPutItem 処理により複数のテーブルが指定できます。

次のレスポンスマッピングテンプレートを使用しましょう。

レスポンスマッピングテンプレート

## If there was an error with the invocation ## there might have been partial results #if($ctx.error) ## Append a GraphQL error for that field in the GraphQL response $utils.appendError($ctx.error.message, $ctx.error.message) #end ## Also returns data for the field in the GraphQL response $utils.toJson($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 コンソールを使用して、locationReadingstemperatureReadings の両方のテーブルにデータが表示されることを確認します。

BatchDeleteItem - センサーの読み取り値の削除

同様に、センサーの読み取り値を一括して削除する必要もあります。これを行うために、Mutation.deleteReadings GraphQL フィールドを使用してみましょう。[Mutation.recordReadings] フィールドの横にある [アタッチ] を選択します。次の画面で、チュートリアルの最初で作成したのと同じ BatchTutorial データソースを選択します。

次のリクエストマッピングテンプレートを使用しましょう。

リクエストマッピングテンプレート

## Convert tempReadings arguments to DynamoDB primary keys #set($tempReadings = []) #foreach($reading in ${ctx.args.tempReadings}) #set($pkey = {}) $util.qr($pkey.put("sensorId", $reading.sensorId)) $util.qr($pkey.put("timestamp", $reading.timestamp)) $util.qr($tempReadings.add($util.dynamodb.toMapValues($pkey))) #end ## Convert locReadings arguments to DynamoDB primary keys #set($locReadings = []) #foreach($reading in ${ctx.args.locReadings}) #set($pkey = {}) $util.qr($pkey.put("sensorId", $reading.sensorId)) $util.qr($pkey.put("timestamp", $reading.timestamp)) $util.qr($locReadings.add($util.dynamodb.toMapValues($pkey))) #end { "version" : "2018-05-29", "operation" : "BatchDeleteItem", "tables" : { "locationReadings": $utils.toJson($locReadings), "temperatureReadings": $utils.toJson($tempReadings) } }

レスポンスマッピングテンプレートは Mutation.recordReadings に使用したものと同じです。

レスポンスマッピングテンプレート

## If there was an error with the invocation ## there might have been partial results #if($ctx.error) ## Append a GraphQL error for that field in the GraphQL response $utils.appendError($ctx.error.message, $ctx.error.message) #end ## Also return data for the field in the GraphQL response $utils.toJson($ctx.result.data)

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

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

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 } } }

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

BatchGetItem - 読み取り値の取得

Pet Health アプリのもう 1 つの一般的な処理として、特定の時刻にセンサーの読み取り値を取得します。スキーマの Query.getReadings GraphQL フィールドにリゾルバーをアタッチしましょう。[Attach (アタッチ)] を選択し、次の画面で、このチュートリアルの最初で作成したのと同じ BatchTutorial データソースを選択します。

次のリクエストマッピングテンプレートを追加しましょう。

リクエストマッピングテンプレート

## Build a single DynamoDB primary key, ## as both locationReadings and tempReadings tables ## share the same primary key structure #set($pkey = {}) $util.qr($pkey.put("sensorId", $ctx.args.sensorId)) $util.qr($pkey.put("timestamp", $ctx.args.timestamp)) { "version" : "2018-05-29", "operation" : "BatchGetItem", "tables" : { "locationReadings": { "keys": [$util.dynamodb.toMapValuesJson($pkey)], "consistentRead": true }, "temperatureReadings": { "keys": [$util.dynamodb.toMapValuesJson($pkey)], "consistentRead": true } } }

BatchGetItem 処理を現在使用していることに注意してください。

SensorReading リストが返されることを選択しているため、レスポンスマッピングテンプレートには多少の相違が発生します。呼び出しの結果を必要な形式にマッピングしましょう。

レスポンスマッピングテンプレート

## Merge locationReadings and temperatureReadings ## into a single list ## __typename needed as schema uses an interface #set($sensorReadings = []) #foreach($locReading in $ctx.result.data.locationReadings) $util.qr($locReading.put("__typename", "LocationReading")) $util.qr($sensorReadings.add($locReading)) #end #foreach($tempReading in $ctx.result.data.temperatureReadings) $util.qr($tempReading.put("__typename", "TemperatureReading")) $util.qr($sensorReadings.add($tempReading)) #end $util.toJson($sensorReadings)

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

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

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 のバッチ処理のデモンストレーションが完了しました。

エラー処理

AWS AppSync では、データソースの処理の部分的な結果が返ることがあります。部分的な結果という用語は、処理の出力がいくつかのデータと 1 つのエラーで構成されていることを表す場合に使用します。エラー処理はアプリケーションで異なるため、AWS AppSync は、エラーを処理する手段をレスポンスマッピングテンプレート内に用意しています。リゾルバーの呼び出しエラーがある場合、これは context から $ctx.error として読み出せます。呼び出しエラーには必ずメッセージと型が含まれています。これらは $ctx.error.message$ctx.error.type というプロパティとしてアクセスできます。レスポンスマッピングテンプレートの呼び出し中に、以下の 3 つの方法で部分的な結果を処理することができます。

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

  2. レスポンスマッピングテンプレートの評価を停止することでエラーを発生させる ($util.error(...) を使用)。データは返さない。

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

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

DynamoDB のバッチ処理

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

このチュートリアルの以前のセクションの 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 で AWS AppSync によって context に呼び出しエラーが設定され、エラーの種類が DynamoDB:ProvisionedThroughputExceededException に設定されています。

  • エラーが存在していても、結果はテーブルごとに $ctx.result.data にマッピングされます。

  • 未処理のキーは $ctx.result.data.unprocessedKeys でアクセス可能です。ここでは、AWS AppSync はテーブルのスループットが不十分なため、キー (sensorId:1, timestamp:2018-02-01T17:21:05.000+08:00) で項目を取得できませんでした。

注意 : BatchPutItem の場合、これは $ctx.result.data.unprocessedItems です。BatchDeleteItem の場合、これは $ctx.result.data.unprocessedKeys です。

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

1. 呼び出しエラーを通知しない

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

記述するレスポンスマッピングテンプレートは通常どおりであり、結果のデータのみを扱います。

レスポンスマッピングテンプレート

$util.toJson($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. エラーを発生させ、テンプレートの実行を中断する

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

レスポンスマッピングテンプレート

## there was an error let's mark the entire field ## as failed and do not return any data back in the response #if ($ctx.error) $util.error($ctx.error.message, $ctx.error.type, null, $ctx.result.data.unprocessedKeys) #end $util.toJson($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. エラーを付加してデータとエラーの両方を返す

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

レスポンスマッピングテンプレート

#if ($ctx.error) ## pass the unprocessed keys back to the caller via the `errorInfo` field $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.data.unprocessedKeys) #end $util.toJson($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. (...)" } ] }