We will create a Lambda function that writes data to the DynamoDB table and uploads images to S3.
This function requires two additional S3 buckets:
Open the template.yaml file in the fcaj-book-shop folder.
Add the following parameters under the Parameters section:
bookImageShopBucketName:
Type: String
Default: book-image-shop-by-tranvix
bookImageResizeShopBucketName:
Type: String
Default: book-image-resize-shop-by-tranvix

Add the S3 bucket resources under the Resources section:
BookImageShop:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref bookImageShopBucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: false
BlockPublicPolicy: false
IgnorePublicAcls: false
RestrictPublicBuckets: false
BookImageResizeShop:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref bookImageResizeShopBucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: false
BlockPublicPolicy: false
IgnorePublicAcls: false
RestrictPublicBuckets: false
CorsConfiguration:
CorsRules:
- AllowedHeaders:
- "*"
AllowedMethods:
- GET
- PUT
- POST
- DELETE
AllowedOrigins:
- "*"

Add the bucket policy for the resize bucket and the BookCreate Lambda function:
BookImageResizeShopPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref BookImageResizeShop
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- "s3:GetObject"
Effect: Allow
Principal: "*"
Resource: !Join
- ""
- - "arn:aws:s3:::"
- !Ref BookImageResizeShop
- /*

Add the BookCreate Lambda function resource:
BookCreate:
Type: AWS::Serverless::Function
Properties:
CodeUri: fcaj-book-shop/book_create
Handler: book_create.lambda_handler
Runtime: python3.13
Environment:
Variables:
BUCKET_NAME: !Ref BookImageShop
BUCKET_RESIZE_NAME: !Ref BookImageResizeShop
TABLE_NAME: !Ref BooksTable
FunctionName: book_create
Architectures:
- x86_64
Policies:
- Statement:
- Sid: BookCreateItem
Effect: Allow
Action:
- dynamodb:PutItem
- s3:PutObject
Resource:
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${booksTableName}
- !Join
- ""
- - "arn:aws:s3:::"
- !Ref BookImageShop
- /*

This resource defines a Lambda function named book_create that:
BUCKET_NAME, BUCKET_RESIZE_NAME, and TABLE_NAMECreate the following directory structure:
fcaj-book-shop
├── fcaj-book-shop
│ ├── books_list
│ │ └── books_list.py
│ └── book_create
│ ├── book_create.py
│ └── requirements.txt
└── template.yaml
import base64
from multipart import MultipartParser
from io import BytesIO
import boto3
import os
BUCKET = os.environ['BUCKET_NAME']
BUCKET_RESIZE = os.environ['BUCKET_RESIZE_NAME']
TABLE = os.environ['TABLE_NAME']
s3_client = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(TABLE)
header_res = {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET,DELETE",
"Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
}
def parse_multipart(body, boundary):
parser = MultipartParser(stream=body, boundary=boundary)
fields = {}
files = {}
for part in parser:
if part.filename:
files[part.name] = {
'file-name': part.filename,
'content': part.raw
}
else:
fields[part.name] = part.value
return fields, files
def upload_to_s3(key, bucket, body):
try:
s3_client.put_object(Key=key, Bucket=bucket, Body=body)
except Exception as e:
print(f"Error uploading image to S3: {e}")
raise Exception(f'Error uploading image to S3: {e}')
def create_new_item(table, item):
try:
table.put_item(Item=item)
except Exception as e:
print(f"Error creating new item in DynamoDB table: {e}")
raise Exception(f'Error creating new item in DynamoDB table: {e}')
def lambda_handler(event, context):
content_type = event['headers'].get(
'Content-Type', '') or event['headers'].get('content-type', '')
try:
body = event['body']
if event['isBase64Encoded']:
body = BytesIO(base64.b64decode(body))
boundary = content_type.split("boundary=")[1]
fields, files = parse_multipart(body, boundary)
# Upload image to s3
file_name = files['image']['file-name']
file_content = files['image']['content']
upload_to_s3(file_name, BUCKET, file_content)
s3_url = f'https://{BUCKET_RESIZE}.s3.amazonaws.com/{file_name}'
# Create new item in DB
item = {
'id': fields.get('id', 0),
'rv_id': 0,
'name': fields.get('name', ''),
'author': fields.get('author', ''),
'price': fields.get('price', ''),
'category': fields.get('category', ''),
'description': fields.get('description', ''),
'image': s3_url
}
create_new_item(table, item)
return {
"statusCode": 200,
"headers": header_res,
"body": "Successfully created item!",
}
except Exception as e:
print(f"Error processing form data: {e}")
raise Exception(f'Error processing form data: {e}')

Create the requirements.txt file in the book_create folder with:
multipart

The multipart library is required for parsing multipart form data (file uploads) sent from the frontend. SAM will install this dependency during sam build.
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 new resources being added:
BookCreateRole (AWS::IAM::Role)BookCreate (AWS::Lambda::Function)BookImageShop (AWS::S3::Bucket)BookImageResizeShop (AWS::S3::Bucket)BookImageResizeShopPolicy (AWS::S3::BucketPolicy)Enter y to confirm the deployment.

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

Open the Lambda console. You should now see both books_list and book_create functions.

Click on the book_create function. Review the Code source section.

Review the Function overview section showing the function diagram.

Click the Configuration tab, then select Environment variables. Verify that the 3 environment variables are set correctly:
BUCKET_NAME = book-image-shop-by-tranvixBUCKET_RESIZE_NAME = book-image-resize-shop-by-tranvixTABLE_NAME = Books
Select Permissions from the left menu. Click on the execution role name (e.g., fcaj-book-shop-BookCreateRole-...).

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

Click on BookCreateRolePolicy0 to expand it. Review the policy JSON that grants dynamodb:PutItem and s3:PutObject permissions.

Open the S3 console. Search for book-image. You should see both buckets created:

Click on book-image-resize-shop-by-tranvix. Switch to the Permissions tab. Verify the Bucket policy allows public s3:GetObject access.

Review the Bucket policy JSON.

Scroll down to the Cross-origin resource sharing (CORS) section. Verify the CORS configuration allows all origins and methods (GET, PUT, POST, DELETE).
