We will create a Lambda function that reads all the data in the DynamoDB table.
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:
Go to the official Python download page: https://www.python.org/downloads/
Download Python 3.13.x (the latest 3.13 release).
Run the installer:
✅ Check “Add python.exe to PATH” at the bottom of the installer.
Click “Install Now” or “Customize installation” if needed.
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

Open the template.yaml file in the fcj-book-shop folder.
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/*

This resource defines a Lambda function named books_list that:
TABLE_NAME referencing the DynamoDB BooksTableCreate the following directory structure:
fcaj-book-shop
├── fcaj-book-shop
│ └── books_list
│ └── books_list.py
└── template.yaml
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)
}

Run the following commands to build and validate the SAM template:
sam validate
sam build

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.

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

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.

Open the Lambda console. Click on the books_list function.

At the books_list function page, you can review:
books_list.py code.

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

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

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

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