Home > database >  Processing DynamoDB Items in GO Lambda
Processing DynamoDB Items in GO Lambda

Time:04-04

I'm performing a simple table Scan to get all items from my DynamoDB table, products. Here is the whole Lambda:

package main

import (
    "context"
    "fmt"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
)

type Product struct {
    productUUID string
    name        string
    description string
    brand       string
    price       string
    category    string
    dateAdded   string
}

func handleRequest() (string, error) {
    cfg, err := config.LoadDefaultConfig(context.TODO(), func(o *config.LoadOptions) error {
        o.Region = "us-east-2"
        return nil
    })

    if err != nil {
        panic(err)
    }

    svc := dynamodb.NewFromConfig(cfg)

    out, err := svc.Scan(context.TODO(), &dynamodb.ScanInput{
        TableName: aws.String("products"),
    })

    if err != nil {
        panic(err)
    }

    for _, item := range out.Items {
        fmt.Println(item)
    }

    return "Items", nil // Temporary debugging return (always 200). I'd like to return a json object here
}

func main() {
    lambda.Start(handleRequest)
}

When I get the data back, I can print out each item in the following form:

map[brand:0xc0002f38a8 category:0xc0002f3848 dateAdded:0xc0002f3830 name:0xc0002f3800 price:0xc0002f37a0 productUUID:0xc0002f3818]

How can I convert these Items into the struct I defined above, and then return the json response that is expected from lambda (basically an HTTP response code and the Items in json form)?

CodePudding user response:

Scanning the table returns the map of items and you want to convert map into the struct so in order to convert the list of maps to the struct you want to use attributevalue.UnmarshalListOfMaps under aws-sdk-go-v2. In the previous version it was in the dynamodbattribute but they decided to change the package in the v2.

products := []Product{}
err = attributevalue.UnmarshalListOfMaps(out.Items, &products)
if err != nil {
    panic(fmt.Sprintf("failed to unmarshal Dynamodb Scan Items, %v", err))
}

productsJson, err := json.Marshal(products)
if err != nil {
    panic(err)
}

One problem I saw in the Product struct is that you need to use exported names and also need to define json tags for the struct fields otherwise the data would not be unmarshalled to the structs. So in order to return the scan items from the tables into the json response your code should look like this.


package main

import (
    "context"
    "encoding/json"
    "fmt"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
    "github.com/aws/aws-sdk-go-v2/service/dynamodb"
)

type Product struct {
    ProductUUID string `json:"productUUID"`
    Name        string `json:"name"`
    Description string `json:"description"`
    Brand       string `json:"brand"`
    Price       string `json:"price"`
    Category    string `json:"category"`
    DateAdded   string `json:"dateAdded"`
}

func handleRequest() (events.APIGatewayProxyResponse, error) {
    products := []Product{}
    cfg, err := config.LoadDefaultConfig(context.TODO(), func(o *config.LoadOptions) error {
        o.Region = "us-east-2"
        return nil
    })

    if err != nil {
        panic(err)
    }
    svc := dynamodb.NewFromConfig(cfg)

    out, err := svc.Scan(context.TODO(), &dynamodb.ScanInput{
        TableName: aws.String("products"),
    })

    if err != nil {
        panic(err)
    }

    err = attributevalue.UnmarshalListOfMaps(out.Items, &products)
    if err != nil {
        panic(fmt.Sprintf("failed to unmarshal Dynamodb Scan Items, %v", err))
    }

    productsJson, err := json.Marshal(products)
    if err != nil {
        panic(err)
    }

    resp := events.APIGatewayProxyResponse{
        StatusCode:      200,
        IsBase64Encoded: false,
        Body:            string(productsJson),
        Headers: map[string]string{
            "Content-Type": "application/json",
        },
    }

    return resp, nil
}

func main() {
    lambda.Start(handleRequest)
}

P.S: Scanning the whole dynamo table and returning it as a response is very costly and you should avoid it.

  • Related