Using API Gateway as DynamoDB Proxy


整理如何使用 Amazon API Gateway 當作 Proxy 直接存取 DynamoDB,而不需要透過 Lambda。

  1. 想法緣由
  2. 準備工作
  3. Rest API:寫入一筆資料
  4. Rest API:取得一筆資料
  5. 測試

1. 想法緣由

一開始我要規劃一對 APIs 對 DynamoDB 存取資料,規劃如下:

  • POST /comment: 寫入一筆 Comment 到 DynamoDB
  • GET /comment/{id}: 從 DynamoDB 取得一筆 Comment
  • 在 Amazon API Gateway 建立 Rest API 如下圖:

大部分的設計直覺就是用 Lambda 實作存取 DynamoDB,然後 API Gateway Call Lambda,如下圖:

但是我在實驗 Developer Guide 範例 Create an API as an Amazon S3 Proxy 時想到,AWS 所有的 Services (EC2, DynamoDB, S3 …) 其實他們都有 API,AWS CLI or SDK 實作了認證機制: Signature Version 4 Signing Process ,讓開發人員直接 Invoke Services API。所以我就嘗試直接 Call DynamoDB API: PutItemGetItem,實驗後發現可行。後來 google 到原來 AWS Blog 也有類似的介紹:Using Amazon API Gateway as a proxy for DynamoDB

下圖是最後實踐出來的樣子,中間沒有 Lambda 了:

2. 準備工作

  1. 建立 DynamoDB Table
  2. 建立 IAM Role 作為 API Gateway 存取 DynamoDB

2-1. 建立 DynamoDB Table

在 DynamoDB 建立一個 Comments 的 Table,可以執行以下 CLI:

1
2
3
4
5
6
aws dynamodb create-table \
--table-name Comments \
--attribute-definitions \
AttributeName=commentId,AttributeType=S \
--key-schema AttributeName=commentId,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

註一:測試時 Read / Write Capacity 給 1 就好,用完記得刪。
註二:這裡方便測試,只有建立 Partition Key,沒有 Sort Key.

2-2. 配置權限 IAM Role

建立 IAM Role:

  1. 允許寫入 DynamoDB
  2. 確認 Trust Relationship 有 API Gateway
  3. 複製 IAM Role ARN,大概會長這樣: arn:aws:iam::123456789:role/Mixo-Dominant-Proxy-Role
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DynamoDB",
"Action": [
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem"
],
"Effect": "Allow",
"Resource": "arn:aws:dynamodb:us-west-2:123456789:table/Comments"
},
{
"Sid": "SaveLambdaLogToCloudWatch",
"Resource": "*",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Effect": "Allow"
}
]
}

3. 寫入一筆資料 (Put an Item)

  1. 建立 Resource: /comment
  2. 新增 Method: POST

3-1. Integration Request

Integration Request 部分,指定以下設定:

  • Integration type: AWS Service
  • AWS Region: 你的 Region
  • AWS Service: DynamoDB
  • HTTP method: POST
  • Action: PutItem
  • Execution role: 2-2 建立的 IAM Role ARN, 他應該長這樣 arn:aws:iam::123456789:role/Mixo-Dominant-Proxy-Role
  • 其他保持預設值
  • 配置如下圖:

同樣在 Integration Request 設定畫面的下方 Body Mapping Templates 設定如下:

  • Request body passthrough 選擇 recommanded
  • 點選 Add mapping template,增加 application/json
  • 點選剛剛增加的 application/json 後,底下的 template 放入以下 JSON,這是給 DynamoDB 的 payload
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "TableName": "Comments",
    "Item": {
    "commentId": { "S": "$context.requestId" },
    "subject": { "S": "$input.path('$.subject')" },
    "author": { "S": "$input.path('$.author')" },
    "message": { "S": "$input.path('$.message')" }
    }
    }

操作設定如下圖:

3-2. Integration Response

  1. 回到 Rest API, 點選右下角的 Integration Response
  2. 展開 Method response status200 的選項,操作設定如下圖:
  3. 展開 Body Mapping Templates -> 點選 Add mapping template -> 新增 application/json -> 設定 Template 如下:
    1
    2
    3
    4
    5
    #set($inputRoot = $input.path('$'))
    {
    "Status": 200,
    "Message": "Success"
    }

操作設定如下圖:

3-3. Test before deployment

在 Rest API 畫面,點選 Test,Request Body 填入以下:

1
2
3
4
5
{
"message": "How are you today?",
"author": "Jack",
"subject": "這是一個新聞"
}

順利的話,Response Body 會回覆 在 Integration Response 設定的 Template,如下:

1
2
3
4
{
"Status": 200,
"Message": "Success"
}

到 DynamoDB 確認是否有資料新增。


4. 取得一筆資料 (Get an Item)

  1. 建立 ResourceName: /comment/{id}
  2. 新增 Method: GET

4-1. Integration Request`

基本步驟同 3-1 描述,注意以下設定:

  • AWS Service: DynamoDB
  • Action: GetItem
  • Body Mapping Template 如下:
    1
    2
    3
    4
    5
    6
    {
    "TableName": "Comments",
    "Key": {
    "commentId": { "S":"$input.params('id')" }
    }
    }

4-2. Integration Response

步驟同 3-1 描述,Body Mapping Template 如下,重新 Format DynamoDB 回傳結果:

1
2
3
4
5
6
#set($inputRoot = $input.path('$'))
{
"message": "$inputRoot.Item.message.S",
"author": "$inputRoot.Item.author.S",
"subject": "$inputRoot.Item.subject.S"
}

Body Mapping Template 使用 Apache Velocity Template Language (VTL) 做資料轉換

4-3. Test before deployment

{id} 填入 test-invoke-request,執行應該會取得以下結果:

1
2
3
4
5
{
"message": "How are you today?",
"author": "Jack",
"subject": "這是一個新聞"
}

5. 測試

依照 Custom Domain Names 的設定後就可以測試了,底下是 Curl 的測試:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
API_HOST=https://api.abc.com

## Put an Item
curl -X POST \
${API_HOST}/forum/comment \
-H 'cache-control: no-cache' \
-H 'content-type: application/json' \
-d '{
"subject": "這是一個新聞",
"author": "Jack",
"message": "How are you today?"
}'

## Get an Item
curl -X GET \
${API_HOST}/forum/comment/e7216f24-4548-11e8-9b25-25aebc0f0f95 \
-H 'cache-control: no-cache'

結論

本文整理如何透過 API Gateway 直接串接 AWS Service,變成類似 Proxy 角色的應用,過程當中省略了原本透過 Lambda 的用法,架構上更精簡。

要注意的是,因為這樣等於間接讓 DynamoDB 暴露出去,所以驗證就很重要,建議要配合 Custom Authorizers 還有 API Key 方式搭配。


延伸閱讀

系列文章

站內延伸

參考資料


Comments