In this step, we will update the code for the book_create function created in the previous workshop:
Open AWS Lambda console, click on book_create function

Copy the following code into lambda_function.py, then click Deploy
The cgi module was completely removed in Python 3.13+. To use the code below, make sure your Lambda function is using Python 3.12 runtime or earlier. You can change the runtime under Configuration tab > Runtime settings > Edit.
import boto3
import json
import base64
import io
import cgi
import os
from decimal import Decimal
# AWS clients
s3 = boto3.client("s3")
dynamodb = boto3.resource("dynamodb")
# ENV variables
UPLOAD_BUCKET = os.environ.get("UPLOAD_BUCKET", "book-image-stores-tranvix")
RESIZE_BUCKET = os.environ.get("RESIZE_BUCKET", "book-image-resize-stores-tranvix")
AWS_REGION = os.environ["AWS_REGION"]
TABLE_NAME = "Books"
def parse_multipart_form(content_type, body):
"""
Parse multipart/form-data from API Gateway (using cgi)
"""
fp = io.BytesIO(base64.b64decode(body))
environ = {
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": content_type,
"CONTENT_LENGTH": str(len(body)),
}
fs = cgi.FieldStorage(
fp=fp,
environ=environ,
keep_blank_values=True
)
return fs
def lambda_handler(event, context):
# ---- CORS headers ----
cors_headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS",
"Access-Control-Allow-Headers": "Content-Type,Authorization"
}
if event.get("httpMethod") == "OPTIONS":
return {
"statusCode": 200,
"headers": cors_headers,
"body": ""
}
try:
headers = event.get("headers", {}) or {}
content_type = headers.get("content-type") or headers.get("Content-Type", "")
table = dynamodb.Table(TABLE_NAME)
body = event.get("body")
if body is None and isinstance(event, dict) and "id" in event:
book_item = event
elif content_type.startswith("application/json"):
book_item = json.loads(body)
# Convert price to string if it's a number (DynamoDB will handle it)
if "price" in book_item and isinstance(book_item["price"], (int, float)):
book_item["price"] = str(book_item["price"])
elif content_type.startswith("multipart/form-data"):
form = parse_multipart_form(content_type, body)
image_file = form["image"]
file_name = image_file.filename
file_bytes = image_file.file.read()
content_type_img = image_file.type or "application/octet-stream"
s3.put_object(
Bucket=UPLOAD_BUCKET,
Key=file_name,
Body=file_bytes,
ContentType=content_type_img
)
image_url = f"https://{RESIZE_BUCKET}.s3.{AWS_REGION}.amazonaws.com/{file_name}"
book_item = {
"id": form["id"].value,
"rv_id": 0,
"name": form["name"].value,
"author": form["author"].value,
"category": form["category"].value,
"price": form["price"].value,
"description": form["description"].value,
"image": image_url
}
else:
return {
"statusCode": 400,
"body": json.dumps({"error": "Unsupported request format"}),
"headers": {**cors_headers, "Content-Type": "application/json"}
}
# -------- Save to DynamoDB --------
table.put_item(Item=book_item)
return {
"statusCode": 200,
"body": json.dumps({
"message": "Book created successfully",
"item": book_item
}),
"headers": {
**cors_headers,
"Content-Type": "application/json"
}
}
except Exception as e:
# Always return CORS headers even on error
return {
"statusCode": 500,
"body": json.dumps({"error": str(e)}),
"headers": {**cors_headers, "Content-Type": "application/json"}
}
This code handles image uploads from users and saves them to S3 bucket

After deploying the code, switch to the Configuration tab to change the runtime

Scroll down to Runtime settings, confirm the runtime is Python 3.13 or higher

On the Edit runtime settings page:

Confirm the runtime has been updated successfully

Configure environment variables for Lambda function

Add the following environment variables:
book-image-stores-tranvix)book-image-resize-stores-tranvix)
Confirm environment variables are updated successfully

Grant Lambda function permission to write files to S3 bucket and DynamoDB

On the IAM Role page, click Add permissions > Create inline policy

Select JSON tab and add the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::book-image-stores-tranvix/*"
},
{
"Effect": "Allow",
"Action": "dynamodb:PutItem",
"Resource": "arn:aws:dynamodb:*:*:table/Books"
}
]
}

Click Next

Enter policy name, e.g., LambdaBookCreatePolicy

Policy created successfully and attached to role
