Mobaxterm
ArticlesCategories
Cloud Computing

From Capital Letter to Serverless Success: A Student Records App Journey

Published 2026-05-13 17:25:06 · Cloud Computing

Building a serverless application on AWS seemed straightforward—until a single capital letter stole six hours of my life. In this Q&A, I share the story of creating a Student Record Management System as part of my AWS Cloud Practitioner journey, covering the full architecture, the bugs I encountered (spoiler: most weren't code-related), and the lessons learned along the way.

What Was the Embarrassing Bug That Took 6 Hours to Find?

The bug that haunted me for six hours was a simple case mismatch in the API endpoint URL. My frontend code was calling /students—all lowercase—while the API Gateway resource was named /Students with a capital 'S'. Every request returned a 404 error. I checked Lambda code, DynamoDB table, API configurations, and redeployed multiple times. Finally, I spotted the discrepancy: that one capital letter. It was a painful reminder that AWS treats resource paths case-sensitively, and even a tiny typo can bring down your entire app. The lesson: always double-check the exact casing of your API paths against your frontend calls.

From Capital Letter to Serverless Success: A Student Records App Journey
Source: dev.to

What Is the Architecture of the Student Record Management System?

The application follows a fully serverless pattern: the user's browser loads a static website hosted on S3, which then communicates with API Gateway. API Gateway triggers one of five Lambda functions (Python 3.12), each responsible for a CRUD operation against a DynamoDB table named StudentRecords. There are no EC2 instances or manual server management. The entire stack scales automatically and stays within the AWS Free Tier. The frontend displays live stats—total students, average GPA, number of unique majors—fetched via the GetAllStudents Lambda. The architecture is simple, cost-effective, and ideal for learning serverless fundamentals on AWS.

Which AWS Services Were Used and What Roles Did They Play?

The system uses five core AWS services, each with a specific role:

  • DynamoDB – Acts as the NoSQL database, storing each student record with a partition key studentId. On-demand capacity ensures pay-per-use pricing.
  • Lambda – Five serverless Python 3.12 functions handle Create, Read, Update, Delete, and List operations. They run only when invoked, reducing costs.
  • API Gateway – Exposes a REST API that routes frontend requests to the appropriate Lambda function. It handles authentication scaling.
  • S3 – Hosts the static frontend (HTML, CSS, JavaScript) as a public website.
  • IAM – Manages permissions, ensuring each Lambda has minimal required access to DynamoDB and other services.

Together, these services form a fully serverless backend without any infrastructure to maintain.

How Was DynamoDB Configured and What Issues Arose?

I created a DynamoDB table named StudentRecords with studentId as the partition key (String type). Capacity was set to on-demand to keep costs low. Each record stores fields like name, email, major, and GPA. A surprising issue came from DynamoDB returning numbers as Python’s Decimal type, not regular floats. When trying to serialize the response to JSON, the Decimal objects caused errors. I had to convert them to floats manually in the Lambda function. Another issue was forgetting to enable DynamoDB Streams for real-time updates (not needed for this project), but the Decimal bug was a major time sink. Always test your serialization when using DynamoDB with Python.

What Were the Lambda Functions and Common Bugs?

I wrote five separate Lambda functions: CreateStudent, GetStudent (by ID), GetAllStudents, UpdateStudent, and DeleteStudent. Each function is a simple Python script that interacts with DynamoDB via the boto3 SDK. A common bug was misconfiguring IAM roles—Lambda couldn't access DynamoDB until I attached the correct policy. Another was incorrect return formats: Lambda expects a specific JSON structure with statusCode and body. I initially forgot to stringify the body, causing API Gateway errors. Also, the GetAllStudents function initially had no pagination, so it failed with large datasets. Adding pagination via DynamoDB’s LastEvaluatedKey solved it.

From Capital Letter to Serverless Success: A Student Records App Journey
Source: dev.to

How Did API Gateway and S3 Fit Into the Picture?

API Gateway acts as the bridge between the frontend and backend. I created a REST API with resource paths like /students and /students/{id}, each linked to the appropriate Lambda function via proxy integration. The challenge was setting up CORS correctly—frontend on S3 using a different origin than API Gateway requires CORS headers in both the API and the Lambda response. As for S3, it hosts the static website (index.html, styles.css, app.js) with public read access enabled. I configured the bucket for static website hosting and noted the endpoint URL. The frontend JavaScript makes fetch calls to the API Gateway URL. Getting the CORS configuration right took several attempts.

What Was the Project Structure?

The project is organized in a simple folder structure on GitHub:

student-record-system/
├── README.md
├── BUGS.md
├── frontend/
│   ├── index.html
│   ├── styles.css
│   └── app.js
└── lambda/
    ├── GetAllStudents/
    │   └── lambda_function.py
    ├── GetStudent/
    │   └── lambda_function.py
    ├── CreateStudent/
    │   └── lambda_function.py
    ├── UpdateStudent/
    │   └── lambda_function.py
    └── DeleteStudent/
        └── lambda_function.py

The README and BUGS.md document the setup and all issues encountered. Each Lambda function is in its own folder with a single Python file. The frontend is a basic single-page application. This structure keeps everything modular and easy to deploy.

What Lessons Were Learned from This Project?

First, case sensitivity matters—that capital letter bug cost six hours. Always match API resource names exactly between frontend and backend. Second, most bugs were not in the code—5 out of 8 issues were configuration-related (IAM permissions, CORS, API Gateway mappings). Third, DynamoDB’s Decimal type requires explicit conversion for JSON serialization. Fourth, always test your IAM policies thoroughly; Lambda needs explicit permissions for every resource. Finally, document your bugs—I created BUGS.md to track issues and solutions, which helped immensely when similar problems reappeared. This project was a humbling but valuable lesson in serverless debugging.