

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

# 教學：使用 Lambda 和 DynamoDB 建立 CRUD HTTP API
<a name="http-api-dynamo-db"></a>

在本教學課程中，您會建立無伺服器 API，以建立、讀取、更新和刪除 DynamoDB 資料表中的項目。DynamoDB 是全受管 NoSQL 資料庫服務，提供快速且可預期的效能，以及無縫的可擴展性。本教學課程大約需要 30 分鐘完成，您可以在 [AWS 免費方案](https://aws.amazon.com/free/)中完成。

首先，您可以使用 DynamoDB 主控台建立 [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) 資料表。然後使用 AWS Lambda 主控台建立 [Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 函數。接著，您可以使用 API Gateway 主控台建立 HTTP API。最後，測試您的 API。

在您叫用 HTTP API 時，API Gateway 會將請求路由至您的 Lambda 函數。Lambda 函數會與 DynamoDB 互動，並傳回 API Gateway 的回應。API Gateway 隨後將回應傳回給您。

![\[您在本教學課程中所建立 HTTP API 的概觀。\]](http://docs.aws.amazon.com/zh_tw/apigateway/latest/developerguide/images/ddb-crud.png)


若要完成此練習，您需要 AWS 帳戶和具有主控台存取權 AWS Identity and Access Management 的使用者。如需詳細資訊，請參閱[設定為使用 API Gateway](setting-up.md)。

在本教學課程中，您需使用 AWS 管理主控台。如需建立此 API 和所有相關資源的 AWS SAM 範本，請參閱 [samples/http-dynamo-tutorial.zip](samples/http-dynamo-tutorial.zip)。

**Topics**
+ [步驟 1：建立 DynamoDB 資料表](#http-api-dynamo-db-create-table)
+ [步驟 2：建立 Lambda 函數](#http-api-dynamo-db-create-function)
+ [步驟 3：建立 HTTP API](#http-api-dynamo-db-create-api)
+ [步驟 4：建立路由](#http-api-dynamo-db-create-routes)
+ [步驟 5：建立整合](#http-api-dynamo-db-create-integration)
+ [步驟 6：將整合連接至路由](#http-api-dynamo-db-attach-integrations)
+ [步驟 7：測試您的 API](#http-api-dynamo-db-invoke-api)
+ [步驟 8：清理](#http-api-dynamo-db-cleanup)
+ [後續步驟：使用 AWS SAM 或 自動化 CloudFormation](#http-api-dynamo-db-next-steps)

## 步驟 1：建立 DynamoDB 資料表
<a name="http-api-dynamo-db-create-table"></a>

您可以使用 [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) 資料表來儲存 API 的資料。

每個項目都有一個唯一的 ID，我們將其作為資料表的[分割區索引鍵](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html#HowItWorks.CoreComponents.PrimaryKey)。

**建立 DynamoDB 資料表**

1. 請在 [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/) 開啟 DynamoDB 主控台。

1. 選擇 **Create Table** (建立資料表)。

1. 對於 **Table name** (資料表名稱)，請輸入 **http-crud-tutorial-items**。

1. 對於 **Partition key (分割區索引鍵)**，請輸入 **id**。

1. 選擇 **Create Table** (建立資料表)。

## 步驟 2：建立 Lambda 函數
<a name="http-api-dynamo-db-create-function"></a>

您可以為您的 API 的後端建立 [Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 函數。此 Lambda 函數會從 DynamoDB 建立、讀取、更新和刪除項目。此函數會使用來自 [API Gateway 的事件](http-api-develop-integrations-lambda.md#http-api-develop-integrations-lambda.proxy-format)來決定如何與 DynamoDB 互動。為了簡單起見，本教學課程使用了單一 Lambda 函數。最佳實務是，您應該為每個路由建立不同的函數。如需 Lambda 的詳細資訊，請參閱 [Lambda Monolith](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/monolith)。

**建立 Lambda 函數**

1. 在以下網址登入 Lambda 主控台：[https://console.aws.amazon.com/lambda](https://console.aws.amazon.com/lambda)。

1. 選擇 **Create function** (建立函數)。

1. 針對 **Function name** (函數名稱)，請輸入 **http-crud-tutorial-function**。

1. 針對**執行期**，請選擇最新支援的 **Node.js** 或 **Python** 執行期。

1. 在 **Permissions** (許可) 下選擇 **Change default execution role** (變更預設執行角色)。

1. 選取**從 AWS 政策範本建立新角色**。

1. 針對 **Role name** (角色名稱)，請輸入 **http-crud-tutorial-role**。

1. 對於 **Policy templates** (政策範本)，請選擇 **Simple microservice permissions**。此政策會授予 Lambda 函數許可，以與 DynamoDB 互動。
**注意**  
本教學課程為了簡單起見，使用受管理政策。最佳實務是，您應建立自己的 IAM 政策以授予所需的最低許可。

1. 選擇 **Create function** (建立函數)。

1. 在主控台的程式碼編輯器中開啟 Lambda 函數，並用下列程式碼取代其內容。選擇 **Deploy** (部署) 以更新您的功能。

------
#### [ Node.js ]

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
  DynamoDBDocumentClient,
  ScanCommand,
  PutCommand,
  GetCommand,
  DeleteCommand,
} from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({});

const dynamo = DynamoDBDocumentClient.from(client);

const tableName = "http-crud-tutorial-items";

export const handler = async (event, context) => {
  let body;
  let statusCode = 200;
  const headers = {
    "Content-Type": "application/json",
  };

  try {
    switch (event.routeKey) {
      case "DELETE /items/{id}":
        await dynamo.send(
          new DeleteCommand({
            TableName: tableName,
            Key: {
              id: event.pathParameters.id,
            },
          })
        );
        body = `Deleted item ${event.pathParameters.id}`;
        break;
      case "GET /items/{id}":
        body = await dynamo.send(
          new GetCommand({
            TableName: tableName,
            Key: {
              id: event.pathParameters.id,
            },
          })
        );
        body = body.Item;
        break;
      case "GET /items":
        body = await dynamo.send(
          new ScanCommand({ TableName: tableName })
        );
        body = body.Items;
        break;
      case "PUT /items":
        let requestJSON = JSON.parse(event.body);
        await dynamo.send(
          new PutCommand({
            TableName: tableName,
            Item: {
              id: requestJSON.id,
              price: requestJSON.price,
              name: requestJSON.name,
            },
          })
        );
        body = `Put item ${requestJSON.id}`;
        break;
      default:
        throw new Error(`Unsupported route: "${event.routeKey}"`);
    }
  } catch (err) {
    statusCode = 400;
    body = err.message;
  } finally {
    body = JSON.stringify(body);
  }

  return {
    statusCode,
    body,
    headers,
  };
};
```

------
#### [ Python ]

```
import json
import boto3
from decimal import Decimal

client = boto3.client('dynamodb')
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table('http-crud-tutorial-items')
tableName = 'http-crud-tutorial-items'


def lambda_handler(event, context):
    print(event)
    body = {}
    statusCode = 200
    headers = {
        "Content-Type": "application/json"
    }

    try:
        if event['routeKey'] == "DELETE /items/{id}":
            table.delete_item(
                Key={'id': event['pathParameters']['id']})
            body = 'Deleted item ' + event['pathParameters']['id']
        elif event['routeKey'] == "GET /items/{id}":
            body = table.get_item(
                Key={'id': event['pathParameters']['id']})
            body = body["Item"]
            responseBody = [
                {'price': float(body['price']), 'id': body['id'], 'name': body['name']}]
            body = responseBody
        elif event['routeKey'] == "GET /items":
            body = table.scan()
            body = body["Items"]
            print("ITEMS----")
            print(body)
            responseBody = []
            for items in body:
                responseItems = [
                    {'price': float(items['price']), 'id': items['id'], 'name': items['name']}]
                responseBody.append(responseItems)
            body = responseBody
        elif event['routeKey'] == "PUT /items":
            requestJSON = json.loads(event['body'])
            table.put_item(
                Item={
                    'id': requestJSON['id'],
                    'price': Decimal(str(requestJSON['price'])),
                    'name': requestJSON['name']
                })
            body = 'Put item ' + requestJSON['id']
    except KeyError:
        statusCode = 400
        body = 'Unsupported route: ' + event['routeKey']
    body = json.dumps(body)
    res = {
        "statusCode": statusCode,
        "headers": {
            "Content-Type": "application/json"
        },
        "body": body
    }
    return res
```

------

## 步驟 3：建立 HTTP API
<a name="http-api-dynamo-db-create-api"></a>

HTTP API 將針對您的 Lambda 函數提供 HTTP 端點。在此步驟中，您將建立空白 API。在下列步驟中，您可以設定路由和整合，以連接您的 API 和 Lambda 函數。



**建立 HTTP API**

1. 在以下網址登入 API Gateway 主控台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 選擇 **Create API** (建立 API)，然後針對 **HTTP API**，選擇 **Build** (建置)。

1. 針對 **API name** (API 名稱)，請輸入 **http-crud-tutorial-api**。

1. 針對 **IP 位址類型**，選擇 **IPv4**。

1. 選擇**下一步**。

1. 對於 **Configure routes** (設定路由)，請選擇 **Next** (下一步) 以略過建立路由。您可以稍後建立路由。

1. 檢閱 API Gateway 為您建立的階段，然後選擇 **Next** (下一步)。

1. 選擇 **Create** (建立)。

## 步驟 4：建立路由
<a name="http-api-dynamo-db-create-routes"></a>

路由是將傳入的 API 請求傳送到後端資源的一種方式。路由由兩部分組成：HTTP 方法和資源路徑，例如 `GET /items`。對於此示例 API，我們建立了四個路由：
+ `GET /items/{id}`
+ `GET /items`
+ `PUT /items`
+ `DELETE /items/{id}`

**若要建立路由**

1. 在以下網址登入 API Gateway 主控台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 選擇您的 API。

1. 選擇 **Routes** (路由)。

1. 選擇 **Create** (建立)。

1. 對於 **Method** (方法)，請選擇 **GET**。

1. 對於路徑，請輸入 **/items/\$1id\$1**。路徑結尾處 `{id}` 是一個路徑參數，是當用戶端發出請求時，API Gateway 從請求路徑中擷取的參數。

1. 選擇 **Create** (建立)。

1. 對 `GET /items`、`DELETE /items/{id}` 和 `PUT /items` 重複步驟 4-7。

![\[您的 API 有 GET /items、GET /items/{id}、DELETE /items/{id} 和 PUT /items 的路由。\]](http://docs.aws.amazon.com/zh_tw/apigateway/latest/developerguide/images/ddb-create-routes.png)


## 步驟 5：建立整合
<a name="http-api-dynamo-db-create-integration"></a>

您可以建立整合以連接到後端資源的路由。在此範例 API 中，您可以建立一個用於所有路由的 Lambda 整合。

**若要建立整合**

1. 在以下網址登入 API Gateway 主控台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 選擇您的 API。

1. 選擇 **Integrations** (整合)。

1. 選擇 **Manage integrations** (管理整合)，然後選擇 **Create** (建立)。

1. 略過 **Attach this integration to a route** (將此整合連接到路由)。您可以在稍後的步驟中完成該操作。

1. 對於 **Integration type** (整合類型)，請選擇 **Lambda function** (Lambda 函數)。

1. 對於 **Lambda function** (Lambda 函數)，請輸入 **http-crud-tutorial-function**。

1. 選擇 **Create** (建立)。

## 步驟 6：將整合連接至路由
<a name="http-api-dynamo-db-attach-integrations"></a>

在此範例 API 中，您可以對所有路由使用相同的 Lambda 整合。將整合連接至所有 API 的路由之後，當用戶端呼叫您的任何路由時，會叫用 Lambda 函數。



**若要將整合連接至路由**

1. 在以下網址登入 API Gateway 主控台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 選擇您的 API。

1. 選擇 **Integrations** (整合)。

1. 選擇路由。

1. 在 **Choose an existing integration** (選擇現有的整合) 下，請選擇 **http-crud-tutorial-function**。

1. 選擇 **Attach integration** (連接整合)。

1. 對所有路由重複步驟 4-6。

所有路由都顯示已連接 AWS Lambda 整合。

![\[主控台會在所有路由 AWS Lambda 上顯示 ，以指出您的整合已連接。\]](http://docs.aws.amazon.com/zh_tw/apigateway/latest/developerguide/images/ddb-attach-integrations.png)


現在您擁有包含路由和整合的 HTTP API，您可以測試您的 API 了。

## 步驟 7：測試您的 API
<a name="http-api-dynamo-db-invoke-api"></a>

為了確保您的 API 正常工作，您可以使用 [curl](https://curl.se)。

**要取得 URL 來叫用您的 API**

1. 在以下網址登入 API Gateway 主控台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 選擇您的 API。

1. 請注意 API 的叫用 URL。它會出現在 **Details** (詳細資訊) 頁面上的 **Invoke URL** (叫用 URL) 下。  
![\[在建立 API 之後，主控台會顯示 API 的叫用 URL。\]](http://docs.aws.amazon.com/zh_tw/apigateway/latest/developerguide/images/ddb-invoke-url.png)

1. 複製您的 API 叫用 URL。

   完整的 URL 如 `https://abcdef123.execute-api.us-west-2.amazonaws.com`。

**若要建立或更新項目**
+ 請使用下列命令來建立或更新項目。該命令包括具有項目 ID、價格和名稱的請求主體。

  ```
  curl -X "PUT" -H "Content-Type: application/json" -d "{\"id\": \"123\", \"price\": 12345, \"name\": \"myitem\"}" https://abcdef123.execute-api.us-west-2.amazonaws.com/items
  ```

**若要取得所有項目**
+ 使用下列命令列出所有項目。

  ```
  curl https://abcdef123.execute-api.us-west-2.amazonaws.com/items
  ```

**若要取得項目**
+ 使用下列命令，透過其 ID 取得項目。

  ```
  curl https://abcdef123.execute-api.us-west-2.amazonaws.com/items/123
  ```

**刪除項目**

1. 使用下列命令刪除項目。

   ```
   curl -X "DELETE" https://abcdef123.execute-api.us-west-2.amazonaws.com/items/123
   ```

1. 取得所有項目，以確認該項目已刪除。

   ```
   curl https://abcdef123.execute-api.us-west-2.amazonaws.com/items
   ```

## 步驟 8：清理
<a name="http-api-dynamo-db-cleanup"></a>

若要避免不必要的成本，請刪除您在此入門練習中建立的資源。採用下列步驟刪除您的 HTTP API、您的 Lambda 函式和相關資源。

**刪除 DynamoDB 資料表**

1. 請在 [https://console.aws.amazon.com/dynamodb/](https://console.aws.amazon.com/dynamodb/) 開啟 DynamoDB 主控台。

1. 選取您的資料表。

1. 選擇 **Delete table** (刪除資料表)。

1. 確認您的選擇，然後選擇 **Delete** (刪除)。

**刪除 HTTP API**

1. 在以下網址登入 API Gateway 主控台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 在 **API** 頁面上，選取 API。選擇 **Actions** (動作)，然後選擇 **Delete** (刪除)。

1. 選擇 **Delete** (刪除)。

**刪除 Lambda 函數**

1. 在以下網址登入 Lambda 主控台：[https://console.aws.amazon.com/lambda](https://console.aws.amazon.com/lambda)。

1. 在 **Functions** (函數) 頁面上，選取函數。選擇 **Actions** (動作)，然後選擇 **Delete** (刪除)。

1. 選擇 **Delete** (刪除)。

**刪除 Lambda 函數的日誌群組**

1. 在 Amazon CloudWatch 主控台中，開啟 [Log groups](https://console.aws.amazon.com/cloudwatch/home#logs:) (日誌群組) 頁面。

1. 在 **Log groups** (日誌群組) 頁面上，選取函數的日誌群組 (`/aws/lambda/http-crud-tutorial-function`)。選擇 **Actions** (動作)，然後選擇 **Delete log group** (刪除日誌群組)。

1. 選擇 **Delete** (刪除)。

**刪除 Lambda 函數的執行角色**

1. 在 AWS Identity and Access Management 主控台中，開啟[角色頁面](https://console.aws.amazon.com/iam/home?#/roles)。

1. 選取函數的角色，例如，`http-crud-tutorial-role`。

1. 選擇 **Delete role** (刪除角色)。

1. 選擇 **Yes, delete** (是，刪除)。

## 後續步驟：使用 AWS SAM 或 自動化 CloudFormation
<a name="http-api-dynamo-db-next-steps"></a>

您可以使用 CloudFormation 或 自動化 AWS 資源的建立和清除 AWS SAM。如需本教學課程的 AWS SAM 範本範例，請參閱 [samples/http-dynamo-tutorial.zip](samples/http-dynamo-tutorial.zip)。

如需範例 CloudFormation 範本，請參閱[範例 CloudFormation 範本](https://github.com/awsdocs/amazon-api-gateway-developer-guide/tree/main/cloudformation-templates)。