建立 DynamoDB
SAM 有定義一個 AWS::Serverless::SimpleTable 的 Resource 給 DynamoDb。
我在 template.yaml 的 Resources 下增加了以下的 code
1 Table:
2 Type: AWS::Serverless::SimpleTable # More info about SimpleTable Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-simpletable.html
3 Properties:
4 PrimaryKey:
5 Name: id
6 Type: String
7 TableName: Users
接著在 SAM Deploy 時,會看到以下的 Change Set
1CloudFormation stack changeset
2-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3Operation LogicalResourceId ResourceType Replacement
4-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5+ Add Table AWS::DynamoDB::Table N/A
6-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
7
8
9Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:129824596365:changeSet/samcli-deploy1711462102/d5561ddb-0426-4de1-be4d-08b4a0ab02aa
10
11
12Previewing CloudFormation changeset before deployment
13======================================================
14Deploy this changeset? [y/N]: y
15
162024-03-26 22:08:41 - Waiting for stack create/update to complete
17
18CloudFormation events from stack operations (refresh every 5.0 seconds)
19-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
20ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
21-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
22UPDATE_IN_PROGRESS AWS::CloudFormation::Stack sam-app User Initiated
23CREATE_IN_PROGRESS AWS::DynamoDB::Table Table -
24CREATE_IN_PROGRESS AWS::DynamoDB::Table Table Resource creation Initiated
25CREATE_COMPLETE AWS::DynamoDB::Table Table -
26UPDATE_COMPLETE_CLEANUP_IN_PROGRESS AWS::CloudFormation::Stack sam-app -
27UPDATE_COMPLETE AWS::CloudFormation::Stack sam-app -
28-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
從 AWS Console 可以看到,我們建立的DynamoDB Table Users
建立 Items
點入 Table Users 以後,右上角的 Action 裡面,有一個 Create Item
接著艾倫會建立一個 item ,id 是 1,firstName 是 Allen ,lastName 是 Hsieh
點下 Create Item
,我們就可以在 Table 裡面看到有第一筆 Item
建立 Get Users API
這邊都是基於 Part1 的 Hello World 去修改
package.json
第一件事情,先在 package.json
加入 aws-sdk lib (line11)
1{
2 "name": "hello_world",
3 "version": "1.0.0",
4 "description": "hello world sample for NodeJS",
5 "main": "app.js",
6 "repository": "https://github.com/awslabs/aws-sam-cli/tree/develop/samcli/local/init/templates/cookiecutter-aws-sam-hello-nodejs",
7 "author": "SAM CLI",
8 "license": "MIT",
9 "dependencies": {
10 "axios": ">=1.6.0",
11 "aws-sdk": "2.1419.0"
12 },
13 "scripts": {
14 "test": "mocha tests/unit/"
15 },
16 "devDependencies": {
17 "chai": "^4.3.6",
18 "mocha": "^10.2.0"
19 }
20}
修改 hello_world 資料夾名稱
接著修改 hello_world 改成 users
1$ tree .
2.
3├── README.md
4├── events
5│ └── event.json
6├── samconfig.toml
7├── template.yaml
8└── users
9 ├── app.mjs
10 ├── package.json
11 └── tests
12 └── unit
13 └── test-handler.mjs
修改 Lambda Function
Line1 : 將 DynamoDBClient 匯入 Line16: 根據 Http Method 來判斷執行什麼 Action ,由於現在只處理 Get Method
1import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2import {
3 DynamoDBDocumentClient,
4 ScanCommand
5} from "@aws-sdk/lib-dynamodb";
6
7const client = new DynamoDBClient({});
8
9const dynamo = DynamoDBDocumentClient.from(client);
10
11const tableName = "Users";
12
13export const lambdaHandler = async (event, context) => {
14 console.log("EVENT: \n" + JSON.stringify(event, null, 2));
15 console.log("httpmethod" + event.httpMethod);
16 let body;
17 switch(event.httpMethod) {
18 case "GET":
19 console.log("Http Get");
20 body = await dynamo.send(
21 new ScanCommand({ TableName: tableName })
22 );
23 body = body.Items;
24 break;
25 }
26 const response = {
27 statusCode: 200,
28 body: JSON.stringify(body)
29 };
30
31 return response;
32};
template.yaml
新增 Get Uesr Function 在 Resource 下面。
1 GetUsers:
2 Type: AWS::Serverless::Function
3 Properties:
4 CodeUri: users/
5 Handler: app.lambdaHandler
6 Runtime: nodejs20.x
7 Architectures:
8 - x86_64
9 Events:
10 HelloWorld:
11 Type: Api
12 Properties:
13 Path: /users
14 Method: get
重新部署
這邊記得,要先 sam build
之後執行 sam deploy
才會生效,接著我們使用 Curl 去打 apigateway
1$ curl https://kxkhl0spi1.execute-api.ap-northeast-1.amazonaws.com/Prod/users
2{"message": "Internal server error"}
這邊會發現,得到的 message 是錯誤,接著去 CloudWatch 看 Log ,會發現 Lambda 沒有權限去 access DynamoDB.
1{
2 "timestamp": "2024-02-10T14:23:01.191Z",
3 "level": "ERROR",
4 "requestId": "def17c42-6b0b-4c96-a081-948984ae0c8f",
5 "message": {
6 "errorType": "DynamoDBServiceException",
7 "errorMessage": "User: arn:aws:sts::129824596365:assumed-role/sam-app-GetUsersRole-e2VmZDAl4NoY/sam-app-GetUsers-PGeiGDJRikRx is not authorized to perform: dynamodb:Scan on resource: arn:aws:dynamodb:ap-northeast-1:129824596365:table/users because no identity-based policy allows the dynamodb:Scan action",
8 "stackTrace": [
9 "AccessDeniedException: User: arn:aws:sts::129824596365:assumed-role/sam-app-GetUsersRole-e2VmZDAl4NoY/sam-app-GetUsers-PGeiGDJRikRx is not authorized to perform: dynamodb:Scan on resource: arn:aws:dynamodb:ap-northeast-1:129824596365:table/users because no identity-based policy allows the dynamodb:Scan action",
10 " at throwDefaultError (/var/runtime/node_modules/@aws-sdk/node_modules/@smithy/smithy-client/dist-cjs/index.js:838:20)",
11 " at /var/runtime/node_modules/@aws-sdk/node_modules/@smithy/smithy-client/dist-cjs/index.js:847:5",
12 " at de_CommandError (/var/runtime/node_modules/@aws-sdk/client-dynamodb/dist-cjs/index.js:2150:14)",
13 " at process.processTicksAndRejections (node:internal/process/task_queues:95:5)",
14 " at async /var/runtime/node_modules/@aws-sdk/node_modules/@smithy/middleware-serde/dist-cjs/index.js:35:20",
15 " at async /var/runtime/node_modules/@aws-sdk/lib-dynamodb/dist-cjs/index.js:174:30",
16 " at async /var/runtime/node_modules/@aws-sdk/node_modules/@smithy/core/dist-cjs/index.js:165:18",
17 " at async /var/runtime/node_modules/@aws-sdk/node_modules/@smithy/middleware-retry/dist-cjs/index.js:320:38",
18 " at async /var/runtime/node_modules/@aws-sdk/middleware-logger/dist-cjs/index.js:33:22",
19 " at async Runtime.lambdaHandler [as handler] (file:///var/task/app.mjs:34:16)"
20 ],
21 "name": "AccessDeniedException",
22 "$fault": "client",
23 "$metadata": {
24 "httpStatusCode": 400,
25 "requestId": "ULUQ6S15FJ9GN2KDDMG9EF7NT3VV4KQNSO5AEMVJF66Q9ASUAAJG",
26 "attempts": 1,
27 "totalRetryDelay": 0
28 },
29 "__type": "com.amazon.coral.service#AccessDeniedException",
30 "message": "User: arn:aws:sts::129824596365:assumed-role/sam-app-GetUsersRole-e2VmZDAl4NoY/sam-app-GetUsers-PGeiGDJRikRx is not authorized to perform: dynamodb:Scan on resource: arn:aws:dynamodb:ap-northeast-1:129824596365:table/users because no identity-based policy allows the dynamodb:Scan action"
31 }
32}
修復權限問題
SAM 有定義許多 policy template ,而 Get 只需要 Read DyanmoDB ,所以我們可以選擇 DynamoDBReadPolicy
就可以
最終 template.yaml 的 Resources 會長這樣
1Resources:
2 Table:
3 Type: AWS::Serverless::SimpleTable
4 Properties:
5 PrimaryKey:
6 Name: id
7 Type: String
8 TableName: Users
9 GetUsers:
10 Type: AWS::Serverless::Function
11 Properties:
12 CodeUri: users/
13 Handler: app.lambdaHandler
14 Runtime: nodejs20.x
15 Architectures:
16 - x86_64
17 Events:
18 HelloWorld:
19 Type: Api
20 Properties:
21 Path: /users
22 Method: get
23 Policies:
24 - DynamoDBReadPolicy:
25 TableName: !Ref Table
部署時,從 ChangeSet 可以看到 IAM Role 有修改
1CloudFormation stack changeset
2-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3Operation LogicalResourceId ResourceType Replacement
4-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5* Modify GetUsersRole AWS::IAM::Role False
6* Modify GetUsers AWS::Lambda::Function False
7* Modify ServerlessRestApi AWS::ApiGateway::RestApi False
8-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
部署完以後再用 Curl 打 ApiGateway 會拿到結果
1$ curl https://kxkhl0spi1.execute-api.ap-northeast-1.amazonaws.com/Prod/users
2[{"id":"1","lastName":"Hsieh","firstName":"Allen"}]
建立 Create User API
修改 Lambda Fuction
艾倫在 swtich case 中,多加了 Post 的 case (line19-33)
1import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2import {
3 DynamoDBDocumentClient,
4 ScanCommand,
5 PutCommand
6} from "@aws-sdk/lib-dynamodb";
7
8const client = new DynamoDBClient({});
9
10const dynamo = DynamoDBDocumentClient.from(client);
11
12const tableName = "Users";
13
14export const lambdaHandler = async (event, context) => {
15 console.log("EVENT: \n" + JSON.stringify(event, null, 2));
16 console.log("httpmethod" + event.httpMethod);
17 let body;
18 switch(event.httpMethod) {
19 case "POST":
20 console.log("Http Post");
21 let item = JSON.parse(event.body)
22 await dynamo.send(
23 new PutCommand({
24 TableName: tableName,
25 Item: {
26 id: item.id,
27 lastName: item.lastName,
28 firstName: item.firstName
29 },
30 })
31 );
32 body = "{}";
33 break;
34 case "GET":
35 console.log("Http Get");
36 body = await dynamo.send(
37 new ScanCommand({ TableName: tableName })
38 );
39 body = body.Items;
40 break;
41 }
42 const response = {
43 statusCode: 200,
44 body: JSON.stringify(body)
45 };
46
47 return response;
48};
修改 template.yaml
這邊要注意的是,因為我們是需要寫入 DynamoDB,所已這邊使用的 Policies 是 DynamoDBCrudPolicy
1 CreateUsers:
2 Type: AWS::Serverless::Function
3 Properties:
4 CodeUri: users/
5 Handler: app.lambdaHandler
6 Runtime: nodejs20.x
7 Architectures:
8 - x86_64
9 Events:
10 HelloWorld:
11 Type: Api
12 Properties:
13 Path: /users
14 Method: post
15 Policies:
16 - DynamoDBCrudPolicy:
17 TableName: !Ref Table
測試
在部署完後,使用 curl 測試 Post ,可以看到正常的回覆。
1curl -X POST https://kxkhl0spi1.execute-api.ap-northeast-1.amazonaws.com/Prod/users -d '{"id":"2", "lastName" : "Chung", "firstName": "Leo"}'
2"{}"
再 call 一次,我們原先寫好的 get ,可以看到剛剛建立的 Item
1$ curl https://kxkhl0spi1.execute-api.ap-northeast-1.amazonaws.com/Prod/users
2[{"id":"2","lastName":"Chung","firstName":"Leo"},{"id":"1","lastName":"Hsieh","firstName":"Allen"}]
評論