DEV Community

Cover image for From Zero to Cloud: Building a Serverless School Management System on AWS
saheed
saheed

Posted on

From Zero to Cloud: Building a Serverless School Management System on AWS

INTRODUCTION

When I started this project, my goal was simple to build something real on the cloud, I spent two weeks building a production ready web application with one simple goal to prove that you don't need expensive servers to ship real applications.

The result? A fully functional school management system that:

  1. Scales from 1 to 1 million requests automatically
  2. Costs less than $2/month to run
  3. Handles real-world problems (CORS, data integrity, query optimization)

This isn't a "Hello World" serverless app. This is a real project with real challenges, real solutions, and real learnings let me show you exactly how I built it.

What We're Building

Before diving into code, here's what the final system does:

User Perspective:

  1. View all school classes in a beautiful card-based interface
  2. Click on a class to see all enrolled students
  3. View detailed student information
  4. Add new students with a form
  5. Everything loads in under 200ms

Behind the Scenes:

  1. Serverless backend with zero servers to manage
  2. Auto-scaling to handle any traffic
  3. NoSQL database optimized for the access patterns

Database Design : Create DynamoDB Tables
First, I went to AWS DynamoDB and created two tables

Why this structure?

  1. ClassID as partition key for direct lookups
  2. GSI on ClassID for querying students by class
  3. Simple, flat structure (no complex joins)

Lambda Functions
I Created Four Lambda Functions
Each function handles one specific operation. I created them in Python 3.12 runtime.

Create REST API

Went to API Gateway console Created new REST API named SchoolManagementAPI

Created resources:

  1. /classes
  2. /classes/{classId}
  3. /classes/{classId}/students
  4. /students
  5. /students/{studentId}

For each resource, created the appropriate HTTP method:

Critical Setting: Enable "Use Lambda Proxy integration" for each method.

Enable CORS

This is where I spent 2 hours debugging! For each resource:

Click "Actions" → "Enable CORS"

Set:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,OPTIONS
Access-Control-Allow-Headers: Content-Type,X-Amz-Date,Authorization

Important: This creates an OPTIONS method automatically for preflight requests.

Deploy API

Click "Actions" → "Deploy API"
Select "[New Stage]"
Name it: dev
Copy the Invoke URL

curl https://school-management-app-saheed-ipaye.s3-website.eu-west-2.amazonaws.com/
/dev/classes
Enter fullscreen mode Exit fullscreen mode

Frontend Deployment

Create S3 Bucket

  1. Went to S3 console
  2. Created bucket: school-management-app-saheed-ipaye
  3. Disabled "Block all public access"

Upload Frontend

  1. Saved React app as index.html
  2. Uploaded to bucket root

Configure Static Website Hosting

  1. Properties tab → Static website hosting
  2. Enable it
  3. Index document: index.html

Setup CloudFront

  1. Create CloudFront distribution
  2. Origin: Your S3 bucket
  3. Redirect HTTP to HTTPS
  4. Default root object: index.html
  5. Deploy (takes 5-15 minutes)

Challenges I Faced

Challenge 1: CORS Errors
Problem: Frontend couldn't call API - "Access-Control-Allow-Origin missing"

Root Cause: CORS needed configuration at BOTH Lambda AND API Gateway

Solution:

  1. Added headers in Lambda responses
  2. Enabled CORS on all API Gateway resources
  3. Created OPTIONS methods
  4. Deployed changes

Challenge 2: Slow Database Queries
Problem: Students not showing up quickly

Root Cause: Using scan() instead of query() on DynamoDB

Solution:

  1. Created Global Secondary Index (GSI) on ClassID
  2. Changed from scan to query operation
  3. Result: 500ms → 50ms (90% improvement!)
# Before (Slow)
response = table.scan(
    FilterExpression='ClassID = :classid',
    ExpressionAttributeValues={':classid': class_id}
)

# After (Fast)
response = table.query(
    IndexName='ClassID-index',
    KeyConditionExpression='ClassID = :classid',
    ExpressionAttributeValues={':classid': class_id}
)

Enter fullscreen mode Exit fullscreen mode

Conclusion

Building this serverless application taught me that:

You don't need expensive infrastructure to ship real apps

✅ Cloud services require thinking differently about architecture
✅ The best learning happens when debugging production issues
✅ Documentation and clean code matter
✅ Shipping something imperfect beats perfecting in isolation

If you're thinking about serverless but haven't started yet Start today. Build something simple, break it, fix it, and learn.

The cloud is the future. Might as well get familiar with it now.

AUTHOR: SAHEED IPAYE

Top comments (0)