Listing Lambda function

We will create a Lambda function that reads all the data in the DynamoDB table.

Prerequisites - Install Python

You must install Python on your machine with the same version as the Lambda runtime specified in the SAM template (in this workshop, we use Python 3.13).

Why is this required?

AWS SAM CLI uses your local Python installation to build the Lambda function’s deployment package. During sam build, SAM installs the dependencies listed in requirements.txt using your local Python runtime. If the Python version on your machine does not match the runtime version specified in template.yaml, the build may fail or produce incompatible packages that cause errors when the Lambda function runs on AWS.

How to install Python 3.13 on Windows:

  1. Go to the official Python download page: https://www.python.org/downloads/

  2. Download Python 3.13.x (the latest 3.13 release).

  3. Run the installer:

    • ✅ Check “Add python.exe to PATH” at the bottom of the installer.

    • Click “Install Now” or “Customize installation” if needed.

  4. After installation, verify the version by opening a terminal and running:

    python --version
    

    You should see output like: Python 3.13.x

For macOS/Linux, you can use pyenv to install and manage multiple Python versions:

# Install pyenv (macOS with Homebrew)
brew install pyenv

# Install Python 3.13
pyenv install 3.13

# Set it as the local version
pyenv local 3.13

Verify your Python version before proceeding:

python --version

Python Version

Edit SAM Template

  1. Open the template.yaml file in the fcj-book-shop folder.

  2. Add the following resource under the Resources section:

    BooksList:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: fcj-book-shop/books_list
        Handler: books_list.lambda_handler
        Runtime: python3.13
        FunctionName: books_list
        Environment:
          Variables:
            TABLE_NAME: !Ref BooksTable
        Architectures:
          - x86_64
        Policies:
          - Statement:
              - Sid: ReadDynamoDB
                Effect: Allow
                Action:
                  - dynamodb:Scan
                  - dynamodb:Query
                Resource:
                  - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${booksTableName}
                  - !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${booksTableName}/index/*
    

    Add BooksList resource

This resource defines a Lambda function named books_list that:

  • Uses Python 3.13 runtime on x86_64 architecture
  • Has an environment variable TABLE_NAME referencing the DynamoDB BooksTable
  • Is granted dynamodb:Scan and dynamodb:Query permissions on the Books table

Create Lambda Function Code

  1. Create the following directory structure:

    fcaj-book-shop
    ├── fcaj-book-shop
    │   └── books_list
    │       └── books_list.py
    └── template.yaml
    
    • Create the fcaj-book-shop/books_list folder inside the fcaj-book-shop directory.
    • Create the books_list.py file and copy the following code into it:
    import json
    import os
    import boto3
    from decimal import *
    from boto3.dynamodb.types import TypeDeserializer
    
    client = boto3.client('dynamodb') 
    serializer = TypeDeserializer()
    
    table_name = os.environ['TABLE_NAME']
    
    class DecimalEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj, Decimal):
                return str(obj)
            return json.JSONEncoder.default(self, obj)
    
    def deserialize(data):
        if isinstance(data, list):
            return [deserialize(v) for v in data]
    
        if isinstance(data, dict):
            try:
                return serializer.deserialize(data)
            except TypeError:
                return {k: deserialize(v) for k, v in data.items()}
        else:
            return data
    
    def lambda_handler(event, context):
        data_books = client.scan(
            TableName=table_name,
            IndexName='name-index'
        )
        format_data_books = deserialize(data_books["Items"])
        for book in format_data_books:
            data_comment = client.query(
                TableName=table_name, 
                KeyConditionExpression="id = :id AND rv_id > :rv_id", 
                ExpressionAttributeValues={
                    ":id": {"S": book['id']}, 
                    ":rv_id": {"N": "0"}
                }
            )
            format_data_comment = deserialize(data_comment['Items'])
            print(data_comment['Items'])
            book["comments"] = format_data_comment
    
        print (format_data_books)
        return {
            "statusCode": 200,
            "headers": {
                "Content-Type": "application/json",
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Methods": "GET,PUT,POST,DELETE, OPTIONS",
                "Access-Control-Allow-Headers": "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method,X-Access-Token,XKey,Authorization"
            },
            "body": json.dumps(format_data_books, cls=DecimalEncoder)
        }
    

    books_list.py source code

Build and Deploy

  1. Run the following commands to build and validate the SAM template:

    sam validate
    sam build
    

    SAM Validate and Build

  2. Deploy the updated stack:

    sam deploy
    

    The changeset will show two new resources being added:

    • BooksListRole (AWS::IAM::Role)
    • BooksList (AWS::Lambda::Function)

    Enter y to confirm the deployment.

    SAM Deploy - Changeset

  3. Wait for the deployment to complete. You should see Successfully created/updated stack.

    Deploy Complete

Verify on AWS Console

  1. Open the CloudFormation console, click on the fcaj-book-shop stack, and switch to the Resources tab. You should now see 5 resources including BooksList and BooksListRole.

    CloudFormation Resources

  2. Open the Lambda console. Click on the books_list function.

    Lambda Functions

  3. At the books_list function page, you can review:

    • The Code source section showing the deployed books_list.py code.

    Code Source

    • The Function overview section showing the function diagram and details.

    Function Overview

  4. Click the Configuration tab, then select Environment variables from the left menu. Verify that TABLE_NAME is set to Books.

    Environment Variables

  5. Select Permissions from the left menu. Click on the execution role name (e.g., fcaj-book-shop-BooksListRole-...).

    Permissions - Execution Role

  6. At the IAM Role page, check the Permissions policies section. You should see:

    • AWSLambdaBasicExecutionRole (AWS managed) - for CloudWatch Logs
    • BooksListRolePolicy0 (Customer inline) - the custom policy we defined

    IAM Role Permissions

  7. Click on BooksListRolePolicy0 to expand it. Review the policy JSON that grants dynamodb:Scan and dynamodb:Query permissions on the Books table.

    BooksListRolePolicy0 JSON