How I Connected a Python App to AWS DynamoDB Using Kiro And Why It Changed How I Think About Cloud Development
What happens when you stop saving data to your computer and start saving it to the AWS cloud? I found out building Part 3 of my Kiro series and it changed how I teach cloud development to beginners.
Have you ever built something that only worked on your own computer?
That was exactly where I found myself after the first two parts of my AWS Kiro series.
We had built a Python Todo application using AWS Kiro, Amazon’s AI-powered IDE. It worked well you could add tasks, list them, and delete them directly from the terminal.
There was just one problem. Every task was stored in a local file.
Close the application, and the data stayed on that computer. Share the project with someone else, and they couldn’t see your tasks. It wasn’t really a cloud application it was simply a local program.
That made me realize something I often see when teaching beginners. Many developers can build an application.
Far fewer know how to take that application into the cloud.
Cloud development often feels intimidating. The moment people hear terms like IAM, DynamoDB, SDKs, permissions, and AWS credentials, many assume it’s going to be difficult.
It doesn’t have to be.
In this part of the series, our goal was simple:
Take a working Python CLI application and replace local file storage with Amazon DynamoDB, while keeping the application itself almost unchanged.
By the end, our Todo application would store every task inside AWS instead of on our laptop, making the project feel much closer to a real production application.
Where We Left Off
If you’ve been following the series, here’s a quick recap.
Part 1 — Building the Todo Application
We used AWS Kiro’s Spec-Driven Development workflow to build a Todo CLI application completely from scratch.
Instead of immediately generating code, Kiro first created three planning documents:
- Requirements
- Design
- Tasks
Only after reviewing and approving those documents did Kiro generate the Python code.
The result was a clean command-line application where users could:
- Add tasks
- View tasks
- Delete tasks
Everything worked exactly as expected.But all the data was stored inside a local JSON file.
Part 2 : Making Kiro Smarter
In the second part, we explored two features that completely changed how I worked with AI-assisted development.
Steering Files
Instead of reminding Kiro about coding standards every time, we created permanent project rules.
These steering files told Kiro things like:
- Follow PEP 8
- Add type hints
- Write docstrings
- Use meaningful variable names
- Keep functions clean and readable
Because these rules lived inside the project, Kiro automatically followed them in every future conversation.
Hooks
We also introduced Hooks. Hooks automatically execute actions whenever something happens inside the project.
For example:
- Save a file
- Run formatting
- Execute tests
- Check code quality
Instead of remembering to run these commands ourselves, Kiro handled them automatically. It felt like having another developer quietly checking our work in the background.
Why Part 3 Matters
This is where everything becomes much more realistic.
Most beginner tutorials stop after building a local application.
Unfortunately, that’s not how software works in the real world.
Real applications don’t store customer information inside a file on someone’s laptop.
Instead, they store information inside databases that live in the cloud.
Once your application uses a cloud database, several important things become possible.
✅ Your data survives after closing the application.
✅ Multiple users can access the same information.
✅ You can use the application from different computers.
✅ You’re following the same architectural pattern used by professional software teams.
This single change transforms a learning project into something that feels much closer to production software.
Choosing the Right Database
AWS offers several database services.
So why did we choose Amazon DynamoDB?
Because it removes almost all of the complexity beginners usually struggle with.
Unlike traditional databases, DynamoDB is fully managed.
That means AWS handles:
- Servers
- Scaling
- Maintenance
- Backups
- High availability
You don’t install anything.
You don’t configure database servers.
You don’t worry about operating systems.
You simply create a table and start storing data.
For someone learning cloud development, that’s incredibly powerful.
What Exactly Is DynamoDB?
If you’ve only worked with SQL databases before, DynamoDB feels slightly different.
Traditional SQL databases organize information into rows and columns.
DynamoDB stores information as items inside a table.
Think of each Todo task as its own document.
For our project, every task contains:
- task_id
- title
- status
That’s all we need.
As our application grows later, we can easily add more fields without redesigning the entire database. This flexibility is one reason DynamoDB is used by applications that handle millions of requests every day.
Why It’s Perfect for Beginners
There were several reasons I chose DynamoDB for this tutorial.
1. Generous Free Tier
For learning projects, you’ll almost never exceed the free limits.
That means you can experiment without worrying about unexpected costs.
2. Setup Takes Only Minutes
Creating a table requires only a few clicks.
No installations.
No configuration files.
No database server.
3. Excellent Python Support
AWS provides boto3, its official Python SDK.
The API is clean, well documented, and integrates naturally with Python applications.
4. Professional Architecture
Although our project is small, the structure we’ll build is the same idea used in production systems.
Instead of mixing database code with application logic, we’ll separate responsibilities.
Our command-line application will focus on interacting with users.
A dedicated database module will handle DynamoDB.
Keeping these responsibilities separate makes applications easier to maintain, easier to test, and easier to expand later.
💡 Quick Tip**One of the biggest habits professional developers build is separating business logic from data access. Even in a small project, this makes future changes dramatically easier.
The Architecture We Were About to Build
Instead of saving tasks directly into a local file, our application would communicate with AWS.
1
2
3
4
5
6
7
8
9
10
Browser / Terminal
│
▼
Python Application (todo.py)
│
▼
Database Layer (db.py)
│
▼
Amazon DynamoDBNothing changes for the user. They still type the same commands.
Behind the scenes, however, every task now lives inside AWS. That single architectural improvement makes the application feel much more like real-world software.
Before Writing Any Code
One lesson I’ve learned while creating tutorials is this:
Most coding problems don’t come from writing code.
They come from missing setup steps.
We’ve all watched tutorials where everything works perfectly for the instructor, but our own project immediately throws an authentication error or a configuration problem because one small prerequisite was never mentioned.
I wanted to avoid that completely.
Before asking Kiro to generate a single line of code, we first prepared our AWS environment properly.
Part 2 — Preparing AWS and Letting Kiro Plan the Upgrade
Now that we knew what we wanted to build, it was time to prepare our AWS environment.
This is the stage many beginners find intimidating.
People often assume cloud development starts with writing code.
In reality, it starts with preparing the environment correctly.
Once the setup is complete, writing the application becomes surprisingly straightforward.
The good news is that for this project, we only needed four things:
- An Amazon DynamoDB table
- An IAM user with the right permissions
- The AWS CLI configured locally
- The boto3 Python SDK
After that, Kiro could take over the implementation.
Step 1: Creating Our DynamoDB Table
The first thing we needed was somewhere to store our tasks.
Inside the AWS Console, we opened Amazon DynamoDB and selected Create Table.
Our configuration was intentionally simple.
Press enter or click to view image in full size
Everything else remained as the default configuration.
Within about a minute, the table status changed to Active.
That’s it.
There were no servers to configure.
No database software to install.
No connection strings to generate.
Our cloud database was ready
Understanding the Partition Key
If you’re new to DynamoDB, the term Partition Key can sound complicated.
It’s actually very simple.
Think of it as the unique ID for every record inside the table.
Every Todo item needs something that uniquely identifies it.
For example:
Press enter or click to view image in full size
Instead of creating these IDs manually, Python generates them automatically using the uuid library.
Every new task receives a completely unique identifier.
This prevents duplicate records and makes deleting or updating tasks much easier later.
Press enter or click to view image in full size
Step 2: Creating an IAM User
This is one of the most important concepts in AWS.
Our Python application needs permission to access DynamoDB.
But giving it full access to our AWS account would be a terrible idea.
Think about giving someone the keys to your house.
If they’re delivering a package, they only need access to the front door — not every room in the house.
AWS works the same way.
Instead of using your own AWS account credentials inside an application, you create a dedicated IAM user with only the permissions it needs.
For this project, I created an IAM user called:
1
todo-app-userI attached one policy:
1
AmazonDynamoDBFullAccess
This allowed the application to read and write items in DynamoDB without granting unnecessary permissions to other AWS services.. It’s a simple example of the Principle of Least Privilege — giving software only the permissions it actually needs.
💡 Quick Tip: In production applications, you should grant only the minimum permissions required. For a learning project, using AmazonDynamoDBFullAccess keeps the setup simple, but in real applications you’d create more restrictive IAM policies.
Step 3: Generating Access Keys
After creating the IAM user, AWS lets you generate an Access Key ID and a Secret Access Key.
These credentials allow applications to authenticate with AWS.
One thing worth remembering:
The Secret Access Key is displayed only once.
If you lose it, AWS cannot show it again.
You’ll need to generate a new one.
Whenever you create credentials, save them securely.
Never upload them to GitHub.
Never hard-code them into your Python files.
Treat them like passwords.
⚠️ Common Mistake
Many beginners accidentally commit AWS credentials into public repositories. Bots continuously scan GitHub looking for exposed AWS keys.
If that happens, someone else could access your AWS resources. Always keep credentials outside your source code.
Step 4: Configuring the AWS CLI
Now that we had credentials, we needed our computer to recognize them.
That’s where the AWS CLI comes in.
Inside the terminal, we simply ran:
1
aws configureAWS asked for four pieces of information.
1
2
3
4
AWS Access Key ID
AWS Secret Access Key
Default Region
Output FormatFor this project, I used:
1
2
Region:us-east-1
Output:jsonOnce those values were entered, the AWS CLI stored them securely on my machine. From this point onward, boto3 could automatically use those credentials without needing them inside the Python code.
This keeps the application cleaner and much more secure.
Press enter or click to view image in full size
Step 5: Installing boto3
With AWS configured, we only needed one Python package.
1
pip install boto3That’s it.
boto3 is AWS’s official Software Development Kit (SDK) for Python.
It provides simple Python classes for interacting with AWS services.
Instead of manually sending HTTP requests to DynamoDB, we can write code like this:
1
table.put_item(...)or
1
table.scan()boto3 handles everything happening behind the scenes. For beginners, this makes AWS much easier to learn.
Updating Our Steering File
One of my favorite parts of this project actually happened before any code was generated.
Back in Part 2 of this series, we created Steering Files.
These files tell Kiro how we want code to be written across the entire project. Since we were introducing DynamoDB, I updated the steering file with a few new rules.
1
## Cloud InfrastructureUse AWS DynamoDB1
Table name: TodoTasksPartition key: task_idUse boto3Region: us-east-1Always use try/exceptStay within AWS Free TierAt first glance, this doesn’t look very exciting.
But it completely changed how Kiro generated code.
Every future file automatically:
- Used the correct table name
- Used boto3
- Connected to the right AWS Region
- Included proper exception handling
- Followed the same project standards
I didn’t need to repeat those instructions again.
Why Steering Files Matter
This is one feature that genuinely changed how I think about AI-assisted development.
Most AI coding tools have short-term memory.
Every new conversation starts fresh.
That means you constantly repeat yourself.
You explain your project structure.
You paste existing code.
You remind the AI about naming conventions.
You explain your preferred libraries.
Then you repeat everything again tomorrow.
Steering Files solve that problem.
You define your project standards once.
Kiro remembers them automatically.
The larger your project becomes, the more valuable this permanent context becomes.
It’s one of those features you don’t fully appreciate until you’ve worked on a project for several weeks.
💡 **Quick Tip**Think of Steering Files as onboarding documents for a new teammate. Once you’ve written the rules, every future conversation starts with that shared understanding.
Finally… Time to Ask Kiro
Now everything was ready.
AWS was configured.
DynamoDB was active.
Permissions were in place.
The steering file had been updated.
Only then did we open Kiro and describe what we wanted.
Our prompt was intentionally simple.
1
2
Upgrade our existing Todo CLI app to use
AWS DynamoDB instead of local storage.1
Requirements:Keep add, list and delete commandsGenerate unique task IDsStore title and statusCreate a separate db.pyKeep todo.py focused on CLI logicHandle AWS errors gracefullyThen something happened that still impresses me every time I use Kiro.
It didn’t generate code.
Instead…
It generated a plan.
Planning Before Coding
Within a few seconds, three new Markdown files appeared.
1
2
3
requirements.md
design.md
tasks.mdEach served a different purpose.
requirements.md
Defined exactly what the upgraded application should do.
design.md
Explained the architecture. It showed how
todo.py would communicate with db.py, which would then interact with DynamoDB.tasks.md
Created a step-by-step implementation checklist. Nothing was skipped. Nothing was out of order.
It felt less like using an AI assistant and more like working with an experienced software engineer who wanted to agree on the design before touching the code.
That planning phase is one of my favorite aspects of Kiro.
Good software rarely starts with typing. It starts with thinking.
What happened next is the part I always ask viewers to watch carefully in the video.
We reviewed all three documents. They were accurate. They reflected exactly what we had described. We clicked Approve.
Then Kiro wrote the code.
What Kiro Actually Buil t — File by File
db.py — The Database Layer
db.py — The Database Layer
Kiro created a new file called db.py to contain all DynamoDB operations. This separation is intentional and important — it means the CLI application does not need to know anything about how data is stored. It just calls the functions. The database layer handles everything else.
Every function has a docstring. Every function has error handling. Every function uses type hints. Because our steering file told Kiro to follow PEP 8 and always include docstrings and type hints — and Kiro followed that instruction precisely, without being reminded.
todo.py — The Updated CLI
The main CLI file was updated to import from db.py instead of reading from a local file. The user-facing commands — add, list, delete — remained identical. The interface did not change at all. Only what happened under the hood changed.
The main CLI file was updated to import from db.py instead of reading from a local file. The user-facing commands — add, list, delete — remained identical. The interface did not change at all. Only what happened under the hood changed.
This is a principle called separation of concerns. The CLI is responsible for presenting information to the user. The database layer is responsible for storing and retrieving it. Neither layer needs to know the implementation details of the other.
Kiro implemented this correctly without being explicitly asked to, because the design document it generated before writing code had already planned this structure.
After Kiro finished writing the code, we ran the app in the terminal.
After Kiro finished writing the code, we ran the app in the terminal.
python todo.py add “Learn AWS with Kiro”
python todo.py list
Both commands worked on the first run. No errors. No debugging. The task was added and listed exactly as before — from the user’s perspective, nothing had changed.
But then we opened the AWS Console. Navigated to DynamoDB. Opened the TodoTasks table. Clicked Explore Table Items.
The task was there.
Not on our computer. Not in a local file. In AWS DynamoDB. In the cloud. With its unique task_id, its title, and its pending status, all exactly as specified.
I always stay quiet in the video at this moment and let viewers see it. Because that moment — seeing your own data sitting in actual cloud infrastructure — is when cloud development stops being abstract and becomes real. That is the moment beginners realise they are doing something that matters.
We closed the app and opened it again. Ran list. The task was still there. Because the data was no longer on our computer. The app could close, restart, be run from a different machine — the data in DynamoDB would remain.
“That is what cloud storage means in practice. Not a concept. Not an analogy. A task you typed on your keyboard, sitting in AWS infrastructure, persistent and accessible from anywhere.”
Four Things This Project Taught That Go Beyond the Code
1. The Database Layer Pattern Is Universal
Creating a separate db.py file for all database operations is not just a preference — it is a pattern that appears in Python projects at every scale. FastAPI apps, Django apps, Flask apps, serverless Lambda functions — they all separate data access from application logic for the same reasons. Learning it here, in a small project, means recognising it immediately when you encounter it in production codebases.
Creating a separate db.py file for all database operations is not just a preference — it is a pattern that appears in Python projects at every scale. FastAPI apps, Django apps, Flask apps, serverless Lambda functions — they all separate data access from application logic for the same reasons. Learning it here, in a small project, means recognising it immediately when you encounter it in production codebases.
2. Error Handling Is Not Optional
Every function in db.py has a try/except block. Our steering file required it, and Kiro implemented it. But the reason matters more than the implementation. Network calls fail. AWS occasionally has service disruptions. Users do things you did not anticipate. An application that crashes with a Python traceback when any of this happens is not a real application. Handling errors gracefully and returning clear messages is part of building things that actually work in the real world.
Every function in db.py has a try/except block. Our steering file required it, and Kiro implemented it. But the reason matters more than the implementation. Network calls fail. AWS occasionally has service disruptions. Users do things you did not anticipate. An application that crashes with a Python traceback when any of this happens is not a real application. Handling errors gracefully and returning clear messages is part of building things that actually work in the real world.
3. Steering Files Compound in Value
The steering file we set up in Part 2 paid dividends in Part 3 without any additional work. We added new rules and Kiro followed all of them — the new DynamoDB rules and the existing Python style rules simultaneously. The more a project grows, the more valuable that permanent context becomes. This is the kind of compounding that makes well-structured development environments worth the upfront investment.
The steering file we set up in Part 2 paid dividends in Part 3 without any additional work. We added new rules and Kiro followed all of them — the new DynamoDB rules and the existing Python style rules simultaneously. The more a project grows, the more valuable that permanent context becomes. This is the kind of compounding that makes well-structured development environments worth the upfront investment.
4. Spec-Driven Development Reduces Rework
We did not debug the code Kiro wrote. Not once. This is not because Kiro is magic — it is because the plan was correct before the code was written. The requirements document captured exactly what we needed. The design document planned the right architecture. The tasks were sequenced correctly. When the plan is right, the implementation is almost always right too. This is what senior engineers mean when they say planning saves time. It does not feel like saving time in the moment. But the absence of a debugging session at the end is the proof.
We did not debug the code Kiro wrote. Not once. This is not because Kiro is magic — it is because the plan was correct before the code was written. The requirements document captured exactly what we needed. The design document planned the right architecture. The tasks were sequenced correctly. When the plan is right, the implementation is almost always right too. This is what senior engineers mean when they say planning saves time. It does not feel like saving time in the moment. But the absence of a debugging session at the end is the proof.
Part 3 ended with a working cloud-connected CLI app. Tasks saved permanently to DynamoDB. Accessible from anywhere. Built entirely through Kiro.
But a CLI app is still a terminal app. You still have to type commands to use it.
The series is continuing from there into deployment taking the app from running on a local machine to running live on the internet, accessible from any device, deployed on AWS infrastructure.
A Note on What Kiro Actually Does
I want to be precise about this because I think it is important. Kiro did not replace any thinking in this project. Every architectural decision — separating the database layer, choosing DynamoDB, planning the function signatures, deciding on error handling patterns — those decisions were made before Kiro wrote a single line.
What Kiro did was implement those decisions correctly, consistently, and quickly. It read our steering files and followed our conventions without being reminded. It planned the implementation in structured documents before writing code. It produced code that we understood, could explain, and could extend.
The difference between using Kiro well and just clicking approve on whatever it generates is exactly the difference between an engineer using a powerful tool and someone hoping a tool will think for them. Kiro amplifies good engineering decisions. It does not replace the need to make them.
That is what I try to teach in this series. Not just how to use Kiro. But how to think about building things properly — and how Kiro can help you execute those thoughts faster and more consistently than working alone.
“Kiro does not replace your thinking. It replaces your typing. The architecture, the decisions, the understanding — those are still yours.”
Resources
📺 Video — Part 3: Connect Your Todo App to AWS DynamoDB Using Kiro → https://youtu.be/T-uIbvwqKOk
📺 Video — Part 1: Build a Todo CLI App with AWS Kiro → https://youtu.be/LicOYks7l58
📺 Video — Part 2: Hooks and Steering Files Explained → https://youtu.be/hv-8klJ7vLE?si=O4LPKYJ1m9Y2F3-K
🔗 AWS Kiro: https://kiro.dev
🔗 AWS DynamoDB Free Tier: https://aws.amazon.com/dynamodb/pricing
🔗 boto3 Documentation: https://boto3.amazonaws.com/v1/documentation/api/latest/index.html
🔗 AWS Free Tier Overview: https://aws.amazon.com/free
🔗 Coding Hives YouTube: https://www.youtube.com/@CodingHives
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article