

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

# Lambda 的 AWS AppSync 解析程式映射範本參考
<a name="resolver-mapping-template-reference-lambda"></a>

**注意**  
我們現在主要支援 APPSYNC\$1JS 執行期及其文件。請考慮[在此處](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html)使用 APPSYNC\$1JS 執行期及其指南。

您可以使用 AWS AppSync 函數和解析程式來調用您帳戶中的 Lambda 函數。您可以在將請求承載傳回給用戶端之前，從 Lambda 函數塑造請求承載和回應。您也可以使用映射範本，向 AWS AppSync 提供關於要叫用之操作性質的提示。本節說明支援 Lambda 操作的不同映射範本。

## 請求映射範本
<a name="request-mapping-template"></a>

Lambda 請求映射範本會處理與 Lambda 函數相關的欄位：

```
{
  "version": string,
  "operation": Invoke|BatchInvoke,
  "payload": any type,
  "invocationType": RequestResponse|Event
}
```

這是解決時 Lambda 請求映射範本的 JSON 結構描述表示法：

```
{
  "definitions": {},
  "$schema": "https://json-schema.org/draft-06/schema#",
  "$id": "https://aws.amazon.com/appsync/request-mapping-template.json",
  "type": "object",
  "properties": {
    "version": {
      "$id": "/properties/version",
      "type": "string",
      "enum": [
        "2018-05-29"
      ],
      "title": "The Mapping template version.",
      "default": "2018-05-29"
    },
    "operation": {
      "$id": "/properties/operation",
      "type": "string",
      "enum": [
        "Invoke",
        "BatchInvoke"
      ],
      "title": "The Mapping template operation.",
      "description": "What operation to execute.",
      "default": "Invoke"
    },
    "payload": {},
    "invocationType": {
      "$id": "/properties/invocationType",
      "type": "string",
      "enum": [
        "RequestResponse",
        "Event"
      ],
      "title": "The Mapping template invocation type.",
      "description": "What invocation type to execute.",
      "default": "RequestResponse"
    }
  },
  "required": [
    "version",
    "operation"
  ],
  "additionalProperties": false
}
```

以下是使用 `invoke`操作的範例，其承載資料是來自 GraphQL 結構描述`getPost`的欄位，以及來自內容的引數：

```
{
  "version": "2018-05-29",
  "operation": "Invoke",
  "payload": {
    "field": "getPost",
    "arguments": $util.toJson($context.arguments)
  }
}
```

整個映射文件會做為 Lambda 函數的輸入傳遞，因此先前的範例現在如下所示：

```
{
  "version": "2018-05-29",
  "operation": "Invoke",
  "payload": {
    "field": "getPost",
    "arguments": {
      "id": "postId1"
    }
  }
}
```

### 版本
<a name="version"></a>

適用於所有請求映射範本，`version`定義範本使用的版本。`version` 為必要項目，且為靜態值：

```
"version": "2018-05-29"
```

### 作業
<a name="operation"></a>

Lambda 資料來源可讓您在 `operation` 欄位中定義兩個操作： `Invoke`和 `BatchInvoke`。`Invoke` 操作 lets AWS AppSync 知道 會為每個 GraphQL 欄位解析程式呼叫 Lambda 函數。`BatchInvoke`instructs AWS AppSync 會批次處理目前 GraphQL 欄位的請求。`operation` 欄位是必要的。

對於 `Invoke`，解析的請求映射範本符合 Lambda 函數的輸入承載。讓我們修改上述範例：

```
{
  "version": "2018-05-29",
  "operation": "Invoke",
    "payload": {
      "arguments": $util.toJson($context.arguments)
    }
}
```

這會解決並傳遞給 Lambda 函數，看起來可能會類似這樣：

```
{
  "version": "2018-05-29",
  "operation": "Invoke",
    "payload": {
      "arguments": {
        "id": "postId1"
      }
    }
}
```

對於 `BatchInvoke`，映射範本會套用至批次中的每個欄位解析程式。為了簡潔起見， AWS AppSync 會將所有解析的映射範本`payload`值合併到符合映射範本的單一物件下的清單中。以下範例範本顯示此合併：

```
{
  "version": "2018-05-29",
  "operation": "BatchInvoke",
  "payload": $util.toJson($context)
}
```

此範本已解析為以下映射文件：

```
{
  "version": "2018-05-29",
  "operation": "BatchInvoke",
  "payload": [
    {...}, // context for batch item 1
    {...}, // context for batch item 2
    {...}  // context for batch item 3
  ]
}
```

`payload` 清單的每個元素都會對應至單一批次項目。Lambda 函數也預期會傳回符合請求中傳送項目順序的清單形狀回應：

```
[
  { "data": {...}, "errorMessage": null, "errorType": null }, // result for batch item 1
  { "data": {...}, "errorMessage": null, "errorType": null }, // result for batch item 2
  { "data": {...}, "errorMessage": null, "errorType": null }  // result for batch item 3
]
```

### 酬載
<a name="payload"></a>

`payload` 欄位是容器，用來將任何格式正確的 JSON 傳遞至 Lambda 函數。如果 `operation` 欄位設定為 `BatchInvoke`， AWS AppSync 會將現有的`payload`值包裝在清單中。此 `payload` 欄位為選用。

### 調用類型
<a name="async-invocation-type"></a>

Lambda 資料來源可讓您定義兩種叫用類型： `RequestResponse`和 `Event`。調用類型與 [Lambda API](https://docs.aws.amazon.com//lambda/latest/api/API_Invoke.html) 中定義的調用類型同義。`RequestResponse` 叫用類型 lets AWS AppSync 會同步呼叫您的 Lambda 函數，以等待回應。`Event` 調用可讓您以非同步方式調用 Lambda 函數。如需 Lambda 如何處理`Event`調用類型請求的詳細資訊，請參閱[非同步調用](https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html)。此 `invocationType` 欄位為選用。如果請求中未包含此欄位， AWS AppSync 會預設為`RequestResponse`叫用類型。

對於任何`invocationType`欄位，解析的請求符合 Lambda 函數的輸入承載。讓我們修改上述範例：

```
{
  "version": "2018-05-29",
  "operation": "Invoke",
  "invocationType": "Event"
  "payload": {
    "arguments": $util.toJson($context.arguments)
  }
}
```

這會解決並傳遞給 Lambda 函數，看起來可能會類似這樣：

```
{
  "version": "2018-05-29",
  "operation": "Invoke",
  "invocationType": "Event",
  "payload": {
    "arguments": {
      "id": "postId1"
    }
  }
}
```

當`BatchInvoke`操作與`Event`調用類型欄位搭配使用時， AWS AppSync 會以上述相同方式合併欄位解析程式，並將請求做為非同步事件傳遞給您的 Lambda 函數，而 `payload`為值清單。我們建議您停用`Event`呼叫類型解析程式的解析程式快取，因為如果有快取命中，這些解析程式不會傳送至 Lambda。

## 回應映射範本
<a name="response-mapping-template"></a>

如同其他資料來源，您的 Lambda 函數會將回應傳送至 AWS AppSync，該回應必須轉換為 GraphQL 類型。

Lambda 函數的結果是在可透過 Velocity 範本語言 (VTL) `$context.result` 屬性取得的 `context` 物件上設定。

如果您的 Lambda 函式回應形狀完全匹配 GraphQL 類型形狀時，您可以使用以下回應映射範本轉送回應：

```
$util.toJson($context.result)
```

沒有任何必要欄位或形狀限制適用於回應映射範本。不過，由於 GraphQL 是強類型，解析後的映射範本必須符合預期的 GraphQL 類型。

## Lambda 函數批次回應
<a name="aws-appsync-resolver-mapping-template-reference-lambda-batched-response"></a>

如果 `operation` 欄位設定為 `BatchInvoke`， AWS AppSync 預期會從 Lambda 函數傳回項目清單。為了讓 AWS AppSync 將每個結果對應回原始請求項目，回應清單的大小和順序必須相符。在回應清單中有`null`項目是有效的；相應地`$ctx.result`設定為 *null*。

## Direct Lambda 解析程式
<a name="direct-lambda-resolvers"></a>

如果您想要完全避免使用映射範本， AWS AppSync 可以為 Lambda 函數提供預設承載，並為 GraphQL 類型提供預設 Lambda 函數回應。您可以選擇提供請求範本、回應範本或兩者，而 AWS AppSync 會相應地處理它。

### Direct Lambda 請求映射範本
<a name="lambda-mapping-template-bypass-request"></a>

未提供請求映射範本時， AWS AppSync 會將`Context`物件直接傳送到 Lambda 函數做為 `Invoke`操作。如需 `Context` 物件結構的詳細資訊，請參閱 [AWS AppSync 解析程式映射範本內容參考](resolver-context-reference.md)。

### Direct Lambda 回應映射範本
<a name="lambda-mapping-template-bypass-response"></a>

未提供回應映射範本時， AWS AppSync 會在收到 Lambda 函數的回應時執行下列其中一項操作。如果您未提供請求映射範本，或者您提供了版本為 的請求映射範本`2018-05-29`，則回應將等同於下列回應映射範本：

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

如果您提供具有 版本的範本`2017-02-28`，回應邏輯函數相當於下列回應映射範本：

```
$util.toJson($ctx.result)
```

實際上，映射範本繞過的運作方式類似於使用某些映射範本，如上述範例所示。不過，在幕後，映射範本的評估會完全規避。由於會略過範本評估步驟，因此與具有需要評估之回應映射範本的 Lambda 函數相比，在某些情況下，應用程式在回應期間可能會遇到較少的額外負荷和延遲。

### Direct Lambda Resolver 回應中的自訂錯誤處理
<a name="lambda-mapping-template-bypass-errors"></a>

您可以透過提出自訂例外狀況，從直接 Lambda 解析程式叫用的 Lambda 函數自訂錯誤回應。下列範例示範如何使用 JavaScript 建立自訂例外狀況：

```
class CustomException extends Error {
  constructor(message) {
    super(message);
    this.name = "CustomException";
  }
}
 
throw new CustomException("Custom message");
```

引發例外狀況時`message`， `errorType`和 分別`errorMessage`是擲回之自訂錯誤的 `name`和 。

如果 `errorType`是 `UnauthorizedException`， AWS AppSync 會傳回預設訊息 (`"You are not authorized to make this call."`)，而不是自訂訊息。

下列程式碼片段是示範自訂 的範例 GraphQL 回應`errorType`：

```
{
  "data": {
    "query": null
  },
  "errors": [
    {
      "path": [
        "query"
      ],
      "data": null,
      "errorType": "CustomException",
      "errorInfo": null,
      "locations": [
        {
          "line": 5,
          "column": 10,
          "sourceName": null
        }
      ],
      "message": "Custom Message"
    }
  ]
}
```

### Direct Lambda 解析程式：啟用批次處理
<a name="lambda-resolvers-batching"></a>

您可以在解析程式`maxBatchSize`上設定 ，為 Direct Lambda Resolver 啟用批次處理。當 `maxBatchSize` 設定為大於 Direct Lambda 解析程式的值`0`時， AWS AppSync 會將請求批次傳送至 Lambda 函數，大小上限為 `maxBatchSize`。

在 Direct Lambda 解析程式`0`上將 `maxBatchSize`設定為 會關閉批次處理。

如需使用 Lambda 解析程式批次處理方式的詳細資訊，請參閱 [進階使用案例：批次處理](tutorial-lambda-resolvers.md#advanced-use-case-batching)。

#### 請求映射範本
<a name="lambda-resolvers-batching-request-template"></a>

啟用批次處理且未提供請求映射範本時， AWS AppSync 會將`Context`物件清單作為`BatchInvoke`操作直接傳送到 Lambda 函數。

#### 回應映射範本
<a name="lambda-resolvers-batching-response-template"></a>

啟用批次處理且未提供回應映射範本時，回應邏輯等同於下列回應映射範本：

```
#if( $context.result && $context.result.errorMessage )
      $utils.error($context.result.errorMessage, $context.result.errorType,
      $context.result.data)
#else
      $utils.toJson($context.result.data)
#end
```

Lambda 函數必須以與傳送的`Context`物件清單相同的順序傳回結果清單。您可以透過`errorType`為特定結果提供 `errorMessage`和 來傳回個別錯誤。清單中的每個結果都有下列格式：

```
{
   "data" : { ... }, // your data
   "errorMessage" : { ... }, // optional, if included an error entry is added to the "errors" object in the AppSync response 
   "errorType" : { ... } // optional, the error type
}
```

**注意**  
目前會忽略結果物件中的其他欄位。

#### 處理來自 Lambda 的錯誤
<a name="lambda-resolvers-batching-error-handling"></a>

您可以在 Lambda 函數中擲回例外狀況或錯誤，以傳回所有結果的錯誤。如果批次請求的承載請求或回應大小太大，Lambda 會傳回錯誤。在這種情況下，您應該考慮減少您的 `maxBatchSize`或減少回應承載的大小。

如需處理個別錯誤的資訊，請參閱 [傳回個別錯誤](tutorial-lambda-resolvers.md#returning-individual-errors)。

#### Lambda 函數範例
<a name="sample-lambda-function"></a>

使用以下結構描述，您可以為`Post.relatedPosts`欄位解析程式建立 Direct Lambda 解析程式，並透過在`maxBatchSize`上方設定 來啟用批次處理`0`：

```
schema {
    query: Query
    mutation: Mutation
}

type Query {
    getPost(id:ID!): Post
    allPosts: [Post]
}

type Mutation {
    addPost(id: ID!, author: String!, title: String, content: String, url: String): Post!
}

type Post {
    id: ID!
    author: String!
    title: String
    content: String
    url: String
    ups: Int
    downs: Int
    relatedPosts: [Post]
}
```

在下列查詢中，將會呼叫 Lambda 函數與批次請求來解析 `relatedPosts`：

```
query getAllPosts {
  allPosts {
    id
    relatedPosts {
      id
    }
  }
}
```

以下是 Lambda 函數的簡單實作：

```
const posts = {
  1: {
    id: '1',
    title: 'First book',
    author: 'Author1',
    url: 'https://amazon.com/',
    content:
      'SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1 SAMPLE TEXT AUTHOR 1',
    ups: '100',
    downs: '10',
  },
  2: {
    id: '2',
    title: 'Second book',
    author: 'Author2',
    url: 'https://amazon.com',
    content: 'SAMPLE TEXT AUTHOR 2 SAMPLE TEXT AUTHOR 2 SAMPLE TEXT',
    ups: '100',
    downs: '10',
  },
  3: { id: '3', title: 'Third book', author: 'Author3', url: null, content: null, ups: null, downs: null },
  4: {
    id: '4',
    title: 'Fourth book',
    author: 'Author4',
    url: 'https://www.amazon.com/',
    content:
      'SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4 SAMPLE TEXT AUTHOR 4',
    ups: '1000',
    downs: '0',
  },
  5: {
    id: '5',
    title: 'Fifth book',
    author: 'Author5',
    url: 'https://www.amazon.com/',
    content: 'SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT AUTHOR 5 SAMPLE TEXT',
    ups: '50',
    downs: '0',
  },
}

const relatedPosts = {
  1: [posts['4']],
  2: [posts['3'], posts['5']],
  3: [posts['2'], posts['1']],
  4: [posts['2'], posts['1']],
  5: [],
}
exports.handler = async (event) => {
  console.log('event ->', event)
  // retrieve the ID of each post
  const ids = event.map((context) => context.source.id)
  // fetch the related posts for each post id
  const related = ids.map((id) => relatedPosts[id])

  // return the related posts; or an error if none were found
  return related.map((r) => {
    if (r.length > 0) {
      return { data: r }
    } else {
      return { data: null, errorMessage: 'Not found', errorType: 'ERROR' }
    }
  })
}
```