Going Serverless: Building a Task Manager with AWS Lambda and DynamoDB
An honest technical account of building a serverless task manager — DynamoDB single-table design, Lambda function architecture in Python, AWS SAM local development, TypeScript frontend integration, and real cost analysis.
Going Serverless: Building a Task Manager with AWS Lambda and DynamoDB
Serverless promises automatic scaling, pay-per-use pricing, and zero server management.
Building the Serverless Task Manager taught me where those promises hold and where the
trade-offs bite. Here is an honest technical account of the architecture, the DynamoDB
data modelling decisions, and what I would change.
Why Serverless for a Task Manager?
Task management apps have a classic variable load pattern: heavy usage during work hours,
near-zero usage overnight and on weekends. A traditional server running 24/7 pays for capacity
it uses 30% of the time. Lambda scales to zero when idle and scales up instantly under load.
For a personal or small-team tool, this means the cost is genuinely near-zero at low usage.
Lambda's free tier covers 1 million requests per month — a task manager for a small team
will likely never exceed this.
System Architecture
Frontend (Next.js on Vercel)
|
v
API Gateway (HTTP API)
|
+--+--+--+--+
| | | |
Lambda Lambda Lambda Lambda
(tasks) (users) (projects) (reports)
| | | |
+--+--+--+--+
|
DynamoDB
(single table)Each Lambda function handles one resource group. API Gateway routes incoming requests to the
appropriate function based on path and HTTP method.
DynamoDB Single-Table Design
This was the steepest learning curve. DynamoDB requires you to think about access patterns
before designing your schema — the opposite of relational modelling.
First, I listed every access pattern the application needs:
1. Get user by ID
2. Get all projects for a user
3. Get all tasks for a project
4. Get all tasks assigned to a user
5. Get task by ID
6. Get tasks by status (todo, in-progress, done)
7. Get tasks due this week
8. Get recent activity for a userThen I designed the table to serve all patterns efficiently:
PK SK Attributes
USER#u_123 PROFILE name, email, createdAt
USER#u_123 PROJECT#p_abc name, description, status
USER#u_123 PROJECT#p_def name, description, status
PROJECT#p_abc TASK#t_001 title, status, assignee, dueDate
PROJECT#p_abc TASK#t_002 title, status, assignee, dueDate
TASK#t_001 METADATA all task fields
USER#u_123 TASK#t_001 (for access pattern 4 - tasks by user)
STATUS#in-progress TASK#t_001 (for access pattern 6 - tasks by status)