

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 在 AWS AppSync 中使用 DynamoDB 批次操作
<a name="tutorial-dynamodb-batch-js"></a>

AWS AppSync 支援在單一區域中的一或多個資料表中使用 Amazon DynamoDB 批次操作。支援的操作包括 `BatchGetItem`、`BatchPutItem` 和 `BatchDeleteItem`。透過使用這些 in AWS AppSync 功能，您可以執行下列任務：
+ 在單一查詢中傳遞金鑰清單，並從資料表傳回結果
+ 在單一查詢中從一或多個資料表讀取記錄
+ 大量寫入記錄至一或多個資料表
+ 有條件地在可能有關聯的多個資料表中寫入或刪除記錄

批次操作 in AWS AppSync 與非批次操作有兩個主要差異：
+ 資料來源角色必須具有解析程式將存取的所有資料表的許可。
+ 解析程式的資料表規格是請求物件的一部分。

## 單一資料表批次
<a name="single-table-batch-js"></a>

**警告**  
`BatchPutItem` 與衝突偵測和解決搭配使用時，不支援 和 `BatchDeleteItem` 。必須停用這些設定，以防止可能的錯誤。

若要開始使用，讓我們建立新的 GraphQL API。在 AWS AppSync 主控台中，選擇從頭開始**建立 API**、**GraphQL APIs **和設計。 ****為您的 API 命名`BatchTutorial API`，選擇**下一步**，然後在**指定 GraphQL 資源**步驟上，選擇**稍後建立 GraphQL 資源**，然後按一下**下一步**。檢閱您的詳細資訊並建立 API。前往**結構描述**頁面並貼上下列結構描述，請注意，對於查詢，我們將傳遞 IDs 清單：

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

這應該會針對您先前所新增的兩個 `id` 值，傳回其結果。請注意，已傳回`null`值`id`為 的 值`3`。這是因為您的`Posts`資料表中尚無具有該值的記錄。另請注意， AWS AppSync 會以與傳遞給查詢的金鑰相同的順序傳回結果，這是 AWS AppSync 代表您執行的額外功能。因此，如果您切換到 `batchGet(ids:[1,3,2])`，您會看到訂單已變更。您也會知道哪個 `id` 傳回 `null` 值。

最後，使用`Posts`資料表做為資料來源，將另一個解析程式連接到 `Mutation.batchDelete` 欄位。使用下列程式碼取代處理常式。這會自動擷取 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 也可讓您跨資料表執行批次操作。來試試建置更複雜的應用程式。試想我們正在建置寵物運作狀態應用程式，其中感應器會報告寵物的位置和身體溫度。感應器是由電池供電，而且每隔幾分鐘就會試著連線到網路。當感應器建立連線時，它會將其讀數傳送至 our 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
}
```

我們需要建立兩個 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
    }
  }
}
```

我們在一個變動中傳送十個感應器讀數，並將讀數分割為兩個資料表。使用 DynamoDB 主控台來驗證資料是否同時顯示在 `locationReadings`和 `temperatureReadings`資料表中。

### BatchDeleteItem - 刪除感應器讀數
<a name="batchdeleteitem-deleting-sensor-readings-js"></a>

同樣地，我們也需要能夠刪除感應器讀數的批次。讓我們使用 `Mutation.deleteReadings` GraphQL 欄位來進行這項動作。在 AWS AppSync 主控台的**結構描述**頁面中，選取 `Mutation.deleteReadings` 欄位旁的**連接**。在下一個畫面上，使用 `locationReadings`資料表做為資料來源來建立您的解析程式。

建立解析程式之後，請將程式碼編輯器中的處理常式取代為下方的程式碼片段。在此解析程式中，我們使用協助程式函數映射器，從提供的輸入`timestamp`中擷取 `sensorId`和 。

```
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 主控台中的**查詢**頁面。現在，讓我們刪除幾個感應器讀數。

執行下列的變動：

```
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`資料表中刪除。

### BatchGetItem - 擷取讀數
<a name="batchgetitem-retrieve-readings-js"></a>

我們應用程式的另一個常見操作是在特定時間點擷取感應器的讀數。試試將解析程式連接到結構描述中的 `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 主控台中的**查詢**頁面。現在，讓我們擷取感應器讀數。

執行下列的查詢：

```
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 中，資料來源操作有時可能會傳回部分結果。我們將會使用部分結果這個詞彙，來表示操作的輸出包含一些資料和錯誤的情況。由於錯誤處理本質上是應用程式特定的， AWS AppSync 讓您有機會處理回應處理常式中的錯誤。如果發生解析程式呼叫錯誤，在文字內容中會出現 `ctx.error`。呼叫錯誤一律包含訊息和類型，可做為 `ctx.error.message` 和 `ctx.error.type` 屬性存取。在回應處理常式中，您可以透過三種方式處理部分結果：

1. 只需傳回資料，即可省略呼叫錯誤。

1. 透過停止處理常式評估來引發錯誤 （使用 `util.error(...)`)，這不會傳回任何資料。

1. 附加錯誤 （使用 `util.appendError(...)`) 並傳回資料。

讓我們使用 DynamoDB 批次操作來示範上述三個點的每一個。

### DynamoDB 批次操作
<a name="dynamodb-batch-operations-js"></a>

使用 DynamoDB 批次操作時，批次作業就有可能部分完成。也就是說，請求的項目或索引鍵可以有一些尚未處理完成。If AWS AppSync 無法完成批次，未處理的項目將在內容上設定調用錯誤。

我們將會使用 `Query.getReadings` 欄位組態 (取自於本教學課程先前區段所說明的 `BatchGetItem` 操作) 來建置錯誤處理功能。這次，讓我們假設在執行 `Query.getReadings` 欄位時，`temperatureReadings` DynamoDB 資料表用盡了佈建的輸送量。DynamoDB 在第二次嘗試 by AWS AppSync `ProvisionedThroughputExceededException`期間引發 ，以處理批次中剩餘的元素。

下列 JSON 代表 DynamoDB 批次調用之後但在呼叫回應處理常式之前序列化的內容：

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

對於內容的幾個注意事項：
+ 調用錯誤已在 `ctx.error` by AWS AppSync 的內容上設定，且錯誤類型已設定為 `DynamoDB:ProvisionedThroughputExceededException`。
+ `ctx.result.data` 即使發生錯誤，也會在 下對應每個資料表的結果。
+ 未處理的金鑰可在 取得`ctx.result.data.unprocessedKeys`。在這裡， AWS AppSync 無法使用金鑰 (sensorId：1，時間戳記：2018-02-01T17：21：05.000\$108：00) 擷取項目，因為資料表輸送量不足。

**注意**  
如果是 `BatchPutItem`，則為 `ctx.result.data.unprocessedItems`。如果是 `BatchDeleteItem`，則為 `ctx.result.data.unprocessedKeys`。

讓我們用三種不同的方式來處理這項錯誤。

#### 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(...)` 是公用程式方法，可讓應用程式設計工具在內容上附加錯誤，而不會干擾回應處理常式的評估，以啟用此行為。評估回應處理常式之後， 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. (...)"
    }
  ]
}
```