Sunday, 8 March 2020

Building Serverless CRUD services in Go with DynamoDB - Part 2

In the previous post, we've created ``createHandler``. In this post, we'll create ``listHandler``. # Getting started First, let's add the config under ``functions`` in ``serverless.yml`` ``` list: handler: bin/handlers/listHandler package: include: - ./bin/handlers/listHandler events: - http: path: iam method: get cors: true ``` Create a file ``listHandler.go`` under src/handlers Similarly, we have the below structure. ``` package main import ( "context" "encoding/json" "fmt" "os" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" ) // TODO1: Define User Struct // TODO2: Define User Response Struct var svc *dynamodb.DynamoDB func init() { region := os.Getenv("AWS_REGION") // Initialize a session if session, err := session.NewSession(&aws.Config{ Region: ®ion, }); err != nil { fmt.Println(fmt.Sprintf("Failed to initialize a session to AWS: %s", err.Error())) } else { // Create DynamoDB client svc = dynamodb.New(session) } } func List(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { var ( tableName = aws.String(os.Getenv("IAM_TABLE_NAME")) ) // TODO3: Add DynamoDB retrival logic } func main() { lambda.Start(List) } ``` For TODO1, this time we don't need omitempty tags because we want to retrieve every field. Ths struct would be ``` type User struct { ID string `json:"id"` UserName string `json:"user_name"` FirstName string `json:"first_name"` LastName string `json:"last_name"` Age int `json:"age"` Phone string `json:"phone"` Password string `json:"password"` Email string `json:"email"` Role string `json:"role"` IsActive bool `json:"is_active"` CreatedAt string `json:"created_at"` ModifiedAt string `json:"modified_at"` DeactivatedAt string `json:"deactivated_at"` } ``` We also need another struct for holding our user response. Let's create a new one and remove TODO2 comment. ``` type Response struct { Response []User `json:"response"` } ``` By doing so, our response should look like ```json { "response": [ { // user record #1 }, { // user record #2 } // and so on ] } ``` When I was coding the response part, one mistake I made was I accidentally added an extra space after ``json:`` like ``` type Response struct { Response []User `json: "response"` } ``` and I got the below result ```json { "Response": [ { // user record #1 }, { // user record #2 } // and so on ] } ``` If there is no json tag or the tag cannot be read, it will reflect the json field name instead. This time we need to use ``svc`` to retrieve the users from DynamoDB. First of all, we need to build the query input parameters ``` params := &dynamodb.ScanInput{ TableName: tableName, } ``` Make the DynamoDB Query API call ``` result, err := svc.Scan(params) if err != nil { fmt.Println("Query API call failed:") fmt.Println((err.Error())) // Status Bad Request return events.APIGatewayProxyResponse{ Body: err.Error(), StatusCode: 400, }, nil } ``` Construct users from response ``` var users []User for _, i := range result.Items { user := User{} if err := dynamodbattribute.UnmarshalMap(i, &user); err != nil { fmt.Println("Got error unmarshalling:") fmt.Println(err.Error()) return events.APIGatewayProxyResponse{ Body: err.Error(), StatusCode: 400, }, nil } users = append(users, user) } ``` Marshal the user response and return APIGatewayProxyResponse ``` body, _ := json.Marshal(&Response{ Response: users, }) return events.APIGatewayProxyResponse{ Body: string(body), StatusCode: 200, }, nil ``` Let's deploy and test it ``` ./scripts/deploy.sh ``` # Testing If you go to AWS Lambda Console, you should see there is a function called ``serverless-iam-dynamodb-dev-get`` ![image](https://user-images.githubusercontent.com/35857179/76139045-a008bc80-6087-11ea-9eea-3654773ef36c.png) You can test your code either in Lambda or API Gateway. Since this is a GET method, a request body is not supported. The response should look like ```json { "response": [ { "id": "bd6fde14-3f6a-4551-95f3-349077a5501f", "user_name": "wingkwong", "first_name": null, "last_name": null, "age": null, "phone": null, "password": "$2a$14$iwyLz8DOnbcolxXezZGXG.uXN9kCxJ8aYzMFftYZ06j1Ybb4uThC2", "email": "wingkwong@gmail.com", "role": "user", "is_active": true, "created_at": "2019-12-28 13:16:41.09607401 +0000 UTC m=+0.077451001", "modified_at": "2019-12-28 13:16:41.096188175 +0000 UTC m=+0.077565137", "deactivated_at": null } ] } ``` That's it for part 2. In the next post, we'll create ``updateHandler.go``.

No comments:

Post a Comment

A Fun Problem - Math

# Problem Statement JATC's math teacher always gives the class some interesting math problems so that they don't get bored. Today t...