

# 教程：使用 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_cn/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. 选择**创建表**。

1. 对于**表名称**，输入 **http-crud-tutorial-items**。

1. 对于**分区键**，输入 **id**。

1. 选择**创建表**。

## 步骤 2：创建 Lambda 函数
<a name="http-api-dynamo-db-create-function"></a>

创建 [Lambda](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html) 函数以用作您的 API 后端。此 Lambda 函数从 DynamoDB 中创建、读取、更新和删除项目。该函数使用 [API Gateway 中的事件](http-api-develop-integrations-lambda.md#http-api-develop-integrations-lambda.proxy-format)来决定如何与 DynamoDB 交互。为简单起见，本教程使用单个 Lambda 函数。作为最佳实践，您应该为每个路由创建单独的函数。有关更多信息，请参阅 [The Lambda monolith](https://serverlessland.com/content/service/lambda/guides/aws-lambda-operator-guide/monolith)（Lambda 单体）。

**创建 Lambda 函数**

1. 通过以下网址登录 Lambda 控制台：[https://console.aws.amazon.com/lambda](https://console.aws.amazon.com/lambda)。

1. 选择**创建函数**。

1. 对于**函数名称**，请输入 **http-crud-tutorial-function**。

1. 在**运行时**中，选择受支持的最新 **Node.js** 或 **Python** 运行时。

1. 在**权限**下，选择**更改默认执行角色**。

1. 选择**从 AWS 策略模板中创建新角色**。

1. 对于**角色名称**，输入 **http-crud-tutorial-role**。

1. 对于**策略模板**，选择 **Simple microservice permissions**。此策略授予 Lambda 函数与 DynamoDB 进行交互的权限。
**注意**  
为简单起见，本教程使用托管策略。作为最佳实践，您应创建自己的 IAM 策略以授予所需的最低权限。

1. 选择**创建函数**。

1. 在控制台的代码编辑器中打开 Lambda 函数，并将其内容替换为以下代码。选择**部署**以更新您的函数。

------
#### [ 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. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 选择**创建 API**，然后为 **HTTP API** 选择**构建**。

1. 对于 **API 名称**，请输入 **http-crud-tutorial-api**。

1. 为 **IP 地址类型**选择 **IPv4**。

1. 选择**下一步**。

1. 对于**配置路由**，选择**下一步**以跳过路由创建。稍后再创建路由。

1. 查看 API Gateway 为您创建的阶段，然后选择**下一步**。

1. 选择**创建**。

## 步骤 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. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 选择 API。

1. 选择**路由**。

1. 选择**创建**。

1. 对于**方法**，选择 **GET**。

1. 对于路径，请输入 **/items/\$1id\$1**。路径结尾的 `{id}` 是 API Gateway 在客户端提出请求时从请求路径中接收的路径参数。

1. 选择**创建**。

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_cn/apigateway/latest/developerguide/images/ddb-create-routes.png)


## 步骤 5：创建集成
<a name="http-api-dynamo-db-create-integration"></a>

您可以创建集成以将路由连接到后端资源。对于此示例 API，您可以创建一个用于所有路由的 Lambda 集成。

**要创建集成**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 选择 API。

1. 选择**集成**。

1. 选择**管理集成**，然后选择**创建**。

1. 跳过**将此集成附加到一个路由**。在后续步骤中再完成此操作。

1. 对于**集成类型**，选择 **Lambda 函数**。

1. 对于 **Lambda 函数**，输入 **http-crud-tutorial-function**。

1. 选择**创建**。

## 步骤 6：将集成附加到路由
<a name="http-api-dynamo-db-attach-integrations"></a>

对于此示例 API，您对所有路由都使用相同的 Lambda 集成。将集成附加到所有的 API 路由后，当客户端调用您的任何路由时，您的 Lambda 函数将被调用。



**要将集成附加到路由**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 选择 API。

1. 选择**集成**。

1. 选择路由。

1. 在**选择现有集成**下，选择 **http-crud-tutorial-function**。

1. 选择**附加集成**。

1. 对所有路由重复步骤 4-6。

所有路由均显示附加了 AWS Lambda 集成。

![\[控制台在所有路由中显示 AWS Lambda，来表示您的集成已附加。\]](http://docs.aws.amazon.com/zh_cn/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)。

**要获取调用 API 的 URL**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 选择 API。

1. 请记下您的 API 的调用 URL。它显示在**详细信息**页面上的**调用 URL** 下。  
![\[创建 API 后，控制台会显示 API 的调用 URL。\]](http://docs.aws.amazon.com/zh_cn/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. 选择**删除表**。

1. 确认您的选择，然后选择**删除**。

**要删除 HTTP API**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 在 **API** 页面上，选择一个 API。选择**操作**，然后选择**删除**。

1. 选择**删除**。

**要删除 Lambda 函数**

1. 通过以下网址登录 Lambda 控制台：[https://console.aws.amazon.com/lambda](https://console.aws.amazon.com/lambda)。

1. 在**函数**页面上，选择一个函数。选择**操作**，然后选择**删除**。

1. 选择**删除**。

**要删除 Lambda 函数的日志组**

1. 在 Amazon CloudWatch 控制台中，打开[日志组页面](https://console.aws.amazon.com/cloudwatch/home#logs:)。

1. 在**日志组**页面上，选择函数的日志组 (`/aws/lambda/http-crud-tutorial-function`)。选择**操作**，然后选择**删除日志组**。

1. 选择**删除**。

**要删除 Lambda 函数的执行角色**

1. 打开‭AWS Identity and Access Management‬控制台中的‭‬[角色页面‭](https://console.aws.amazon.com/iam/home?#/roles)。

1. 选择函数的角色，例如 `http-crud-tutorial-role`。

1. 选择**删除角色**。

1. 选择**是，删除**。

## 下一步：使用 AWS SAM 或 CloudFormation 实现自动化
<a name="http-api-dynamo-db-next-steps"></a>

您可以使用 CloudFormation 或 AWS SAM 自动创建和清理AWS资源。如需本教程中所用的 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)。