A full-stack e-commerce platform built for AWS Academy Learner Lab. Features a Node.js/Express REST API backend, a single-page storefront frontend, and a complete AWS infrastructure stack provisioned via CloudFormation.
Team: Cloud Catalyst | Course: MTech BITS
- Architecture Overview
- Project Structure
- Tech Stack
- Local Development
- Environment Variables
- API Reference
- Database Schema
- AWS Infrastructure
- Deployment Guide
- Running Tests
- Cost Estimate
Browser
└── CloudFront CDN (HTTPS)
├── /index.html → S3 Static Assets Bucket (frontend)
└── /api/* → Application Load Balancer (HTTP)
└── Auto Scaling Group (EC2 t3.micro)
└── Node.js API (port 3000)
└── Aurora MySQL 8.0 (private subnet)
- WAF v2 sits in front of the ALB with AWS Managed Rules (Common + Known Bad Inputs)
- EC2 instances are in private subnets; only the ALB is internet-facing
- Database is isolated in private subnets, accessible only from EC2 security group
.
├── backend/ # Node.js REST API
│ ├── server.js # Entry point — Express app bootstrap
│ ├── package.json
│ ├── .env.example # Environment variable template
│ ├── src/
│ │ ├── config/
│ │ │ └── db.js # MySQL connection pool
│ │ ├── middleware/
│ │ │ ├── auth.js # JWT Bearer token verification
│ │ │ └── adminOnly.js # Admin role guard
│ │ ├── routes/
│ │ │ ├── auth.js # /api/auth
│ │ │ ├── products.js # /api/products
│ │ │ ├── cart.js # /api/cart
│ │ │ └── orders.js # /api/orders
│ │ ├── controllers/
│ │ │ ├── authController.js
│ │ │ ├── productController.js
│ │ │ ├── cartController.js
│ │ │ └── orderController.js
│ │ └── migrations/
│ │ └── init.js # Auto-run schema creation on startup
│ └── tests/
│ ├── authController.test.js
│ ├── productController.test.js
│ └── middleware.test.js
├── frontend/
│ └── index.html # Single-page storefront (no build step)
├── cloudformation/
│ ├── infrastructure.yaml # Full AWS stack definition
│ ├── DEPLOYMENT.md # Step-by-step deployment guide
│ └── RUNBOOK.md # Quick-reference checklist
└── README.md
| Layer | Technology |
|---|---|
| Runtime | Node.js 18+ |
| Framework | Express 4 |
| Database | Aurora MySQL 8.0 (mysql2 driver) |
| Auth | JWT (jsonwebtoken) + bcryptjs |
| Security | Helmet, CORS, express-rate-limit |
| Frontend | Vanilla HTML/CSS/JS (no build step) |
| Infrastructure | AWS CloudFormation |
| CDN | AWS CloudFront |
| Compute | EC2 t3.micro + Auto Scaling Group |
| Load Balancer | Application Load Balancer |
| Storage | S3 (static assets + media) |
| WAF | AWS WAF v2 |
| Testing | Jest |
- Node.js 18+
- A running MySQL instance (local or RDS)
# 1. Install dependencies
cd backend
npm install
# 2. Configure environment
cp .env.example .env
# Edit .env with your local DB credentials and a JWT secret
# 3. Start development server (auto-restarts on changes)
npm run devServer starts at http://localhost:3000. The database schema is created automatically on first startup.
Copy backend/.env.example to backend/.env and fill in the values:
| Variable | Description | Example |
|---|---|---|
NODE_ENV |
Environment mode | production |
PORT |
Server port | 3000 |
DB_HOST |
Aurora cluster endpoint | cluster.us-east-1.rds.amazonaws.com |
DB_PORT |
MySQL port | 3306 |
DB_NAME |
Database name | ecommerce |
DB_USER |
Database username | admin |
DB_PASSWORD |
Database password | YourSecurePass123! |
JWT_SECRET |
256-bit secret for JWT signing | a-long-random-string |
JWT_EXPIRES_IN |
Token expiry | 7d |
AWS_REGION |
AWS region | us-east-1 |
S3_MEDIA_BUCKET |
Media S3 bucket name | ecommerce-infra-media-123456 |
CORS_ORIGIN |
Allowed CORS origin (optional) | https://your-cloudfront-domain |
GET /healthReturns server status and uptime. Used by the ALB health check.
POST /api/auth/register
Content-Type: application/json
{
"name": "Jane Doe",
"email": "jane@example.com",
"password": "securepass123"
}POST /api/auth/login
Content-Type: application/json
{
"email": "jane@example.com",
"password": "securepass123"
}Both return a JWT token and user object on success.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| GET | /api/products |
Public | List products (paginated) |
| GET | /api/products/:id |
Public | Get single product |
| POST | /api/products |
Admin only | Create product |
Query params for listing: page, limit, category, search
POST /api/products
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Laptop",
"description": "High-performance laptop",
"price": 999.99,
"stock_quantity": 50,
"category": "electronics",
"image_url": "https://example.com/laptop.jpg"
}All cart routes require a valid JWT token.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/cart |
Get current user's cart with totals |
| POST | /api/cart |
Add item to cart (validates stock) |
| DELETE | /api/cart/:id |
Remove item from cart |
POST /api/cart
Authorization: Bearer <token>
Content-Type: application/json
{
"product_id": 1,
"quantity": 2
}All order routes require a valid JWT token.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/orders |
List current user's orders |
| POST | /api/orders |
Create order from cart (transactional) |
| GET | /api/orders/:id |
Get order details (owner or admin) |
Creating an order atomically: validates stock, inserts order + line items, decrements stock, and clears the cart — all in a single transaction.
All errors follow a consistent format:
{ "error": "Error message here" }| Status | Meaning |
|---|---|
| 400 | Validation error |
| 401 | Missing or invalid token |
| 403 | Insufficient permissions |
| 404 | Resource not found |
| 409 | Conflict (e.g. email already registered) |
| 500 | Internal server error |
Schema is created automatically on server startup (idempotent — safe to run multiple times).
| Table | Description |
|---|---|
users |
User accounts with bcrypt-hashed passwords and role (user / admin) |
products |
Product catalog with pricing, stock, and category |
cart |
Per-user cart items (unique constraint on user + product) |
orders |
Order records with status (pending → delivered / cancelled) |
order_items |
Line items linking orders to products with quantity and price snapshot |
The full stack is defined in cloudformation/infrastructure.yaml as a single CloudFormation stack.
Networking
- VPC (
10.0.0.0/16) with 2 public + 2 private subnets across 2 AZs - Internet Gateway + route tables
Security
- ALB Security Group: HTTP (80) + HTTPS (443) from internet
- EC2 Security Group: port 3000 from ALB only, SSH (22) optional
- DB Security Group: MySQL (3306) from EC2 only
- WAF v2 with AWS Managed Rules (Common + Known Bad Inputs)
Compute
- EC2 Launch Template (Amazon Linux 2023, t3.micro)
- Auto Scaling Group (min 1, max 2) with CPU-based scaling at 60%
- Application Load Balancer (internet-facing, HTTP:80)
- ALB Target Group with
/healthhealth checks
Database
- Aurora MySQL 8.0 cluster (
db.t3.medium) - Read replica created only in
prodmode (skipped indevto save credits) - 7-day automated backups, storage encrypted off (Learner Lab constraint)
Storage & CDN
- S3 Static Assets Bucket (versioned, private) — serves
frontend/index.html - S3 Media Bucket (private, lifecycle to STANDARD_IA after 30 days) — stores
backend.zip - CloudFront Distribution with OAC → S3 (HTTPS redirect, PriceClass_100)
IAM
- EC2 Instance Role: S3 read, CloudWatch Logs, CloudWatch Metrics
- CodeDeploy Role: EC2, ASG, ELB, SNS, CloudWatch permissions
| Parameter | Default | Notes |
|---|---|---|
Environment |
dev |
dev skips read replica to save ~$0.66/day |
KeyPairName |
(empty) | Optional — fill only if you need SSH access |
DBMasterUsername |
admin |
Alphanumeric, starts with a letter |
DBMasterPassword |
(required) | 8–41 chars, alphanumeric + special chars |
After deployment, the stack exports:
ALBDNSName— backend URLCloudFrontDomain— frontend CDN URLAuroraClusterEndpoint— DB writer endpointStaticAssetsBucketName— uploadindex.htmlhereMediaBucketName— uploadbackend.ziphere
See cloudformation/DEPLOYMENT.md for the full step-by-step guide and cloudformation/RUNBOOK.md for a quick checklist.
# 1. Create CloudFormation stack (~15–20 min)
# Upload cloudformation/infrastructure.yaml via AWS Console
# Stack name: ecommerce-infra
# Environment: dev
# 2. Package and upload backend
cd backend
npm install
zip -r backend.zip . -x "node_modules/*" ".env"
# Upload backend.zip to the MediaBucket S3 bucket
# 3. Restart EC2 instances
# EC2 → Auto Scaling Groups → terminate instances
# New instances pull backend.zip and start automatically
# 4. Deploy frontend
# Edit frontend/index.html — set API base to your CloudFront domain
# Upload index.html to the StaticAssetsBucket S3 bucket
# 5. Verify
curl http://<ALBDNSName>/health
# → { "status": "healthy", ... }When done testing, delete the stack to stop all billing:
CloudFormation → select stack → Delete
Unit tests run locally against mocked dependencies (no AWS credits needed).
cd backend
npm testTest coverage:
| File | Tests |
|---|---|
authController.test.js |
Register/login validation, duplicate email, token generation |
productController.test.js |
Pagination, 404 handling, product creation validation |
middleware.test.js |
JWT verification, admin role enforcement |
See backend/TESTS.md for the full test list.
Running in dev mode on AWS Academy Learner Lab (with $35 credit):
| Resource | Cost/hr | Daily (8 hrs) |
|---|---|---|
| EC2 2× t3.micro | $0.021 | $0.17 |
| Aurora MySQL db.t3.medium | $0.082 | $0.66 |
| ElastiCache Redis cache.t3.micro | $0.034 | $0.27 |
| Application Load Balancer | $0.023 | $0.18 |
| S3 + CloudFront | ~$0.01 | $0.08 |
| WAF | ~$0.21 | $1.68 |
| Total (dev mode) | ~$0.38/hr | ~$3.04/day |
Budget planning: 10 days of testing at 8 hrs/day ≈ $30. Always delete the stack when not actively testing.
MIT