Production: http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com
Local: http://localhost
- Authentication
- User Management
- Course Management
- Module Management
- Practice Quiz
- Notifications
- Health Checks
All protected endpoints require a JWT token in the Authorization header:
Authorization: Bearer <your_jwt_token>
- Register a user via
/user/register - Login via
/user/loginto receive JWT token - Use token in subsequent requests
Endpoint: POST /user/register
Description: Creates a new user account with hashed password, sets up notification preferences, and sends welcome email.
Authentication: None (Public)
Request Body:
{
"Name": "John Doe",
"Phone": "9876543210",
"Email": "john.doe@example.com",
"Profession": "Software Engineer",
"Password": "securePassword123"
}Validation Rules:
Name: 3-25 charactersPhone: Minimum 10 charactersEmail: Valid email formatProfession: Minimum 3 charactersPassword: Minimum 6 characters
Response: 201 Created
{
"message": "User registered successfully",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}What Happens:
- Generates unique UUID for user
- Hashes password with bcrypt
- Stores user in PostgreSQL
UserandAuthtables - Creates user profile in DynamoDB (Notification service)
- Sends registration confirmation email via SNS → SQS → Lambda
Error Responses:
500: Database error or email already exists
Endpoint: POST /user/login
Description: Authenticates user credentials and returns JWT token valid for 1 hour.
Authentication: None (Public)
Request Body:
{
"Email": "john.doe@example.com",
"Password": "securePassword123"
}Response: 200 OK
{
"message": "Login successful",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}What Happens:
- Queries
Authtable for email - Verifies password hash using bcrypt
- Generates JWT token with userId and 1-hour expiration
- Returns token for subsequent authenticated requests
Error Responses:
401: Invalid email or password500: Internal server error
Endpoint: GET /user/profile
Description: Retrieves authenticated user's profile information including enrolled courses.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Response: 200 OK
{
"Name": "John Doe",
"Profession": "Software Engineer",
"Phone": "9876543210",
"Email": "john.doe@example.com"
}What Happens:
- Validates JWT token and extracts userId
- Checks Redis cache for profile data
- If cache miss, queries PostgreSQL
Usertable - Caches result in Redis for faster subsequent requests
- Returns user profile
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: User not found500: Internal server error
Endpoint: POST /user/delete
Description: Permanently deletes authenticated user's account and all associated data, then sends a confirmation email.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Response: 200 OK
{
"message": "Account deleted successfully"
}What Happens:
- Validates JWT token and extracts userId
- Sends delete event to DB API
- DB API sends event to SQS queue and clears Redis cache
- Lambda consumer processes event:
- Deletes user from
Usertable - CASCADE automatically deletes from:
Auth(login credentials)UserCourse(course enrollments)UserModuleProgress(module progress)Quiz(quiz attempts)PracticeQuiz(practice quiz statistics)
- Deletes user from
- Sends notification request to Notification API
- Notification API:
- Retrieves user data from DynamoDB
- Fetches "AccountDeletion" email template
- Formats confirmation message
- Publishes to SNS → SQS → Lambda sends email
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions500: Internal server error
Note: This action is irreversible. All user data will be permanently deleted.
Endpoint: POST /course/purchase
Description: Enrolls authenticated user in a course, granting access to all modules, and sends a course purchase notification email.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Request Body:
{
"courseName": "Python Fundamentals"
}Response: 201 Created
{
"message": "Course 'Python Fundamentals' purchased successfully by user 123e4567-e89b-12d3-a456-426614174000"
}What Happens:
- Validates JWT token and extracts userId
- Sends purchase event to DB API
- DB API processes event:
- Looks up courseId from
Coursetable - Inserts record into
UserCoursetable
- Looks up courseId from
- Sends notification request to Notification API with CourseName
- Notification API sends "CoursePurchase" email to user
- Caches purchase in Redis
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: Course not found500: Internal server error
Endpoint: GET /course/progress?courseName=<course_name>
Description: Fetches course progress statistics showing completed, in-progress, and pending modules.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Query Parameters:
courseName(required): Name of the course
Example:
GET /course/progress?courseName=Python%20Fundamentals
Response: 200 OK
{
"inProgress": {
"Introduction to Python": ["intro", "basics", "functions"],
"Variables and Data Types": ["variables", "types"]
},
"completed": [
"Control Flow",
"Loops",
"Functions Advanced"
]
}Response Fields:
inProgress: Dictionary mapping module names to arrays of completed pages (TEXT[])completed: Array of completed module names
What Happens:
- Validates JWT token and extracts userId
- Checks Redis cache for progress data
- If cache miss, sends request to DB API
- DB API queries:
- Gets courseId from
Coursetable - Queries
UserModuleProgressjoined withModuletable:- For completed modules: Returns module names where
Completed= TRUE - For in-progress modules: Returns module names and
CompletedPagearrays whereCompleted= FALSE
- For completed modules: Returns module names where
- Gets courseId from
- Returns dictionary with in-progress modules (with page arrays) and completed module names
- Caches result in Redis
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: Course not found500: Internal server error
Endpoint: GET /course/enrolled
Description: Retrieves list of all courses the authenticated user has enrolled in.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Response: 200 OK
{
"courses": [
"Python Fundamentals",
"Advanced JavaScript",
"Data Structures"
]
}What Happens:
- Validates JWT token and extracts userId
- Checks Redis cache for enrolled courses
- If cache miss, queries DB API
- DB API joins
UserCourseandCoursetables to get course names - Caches result in Redis for faster subsequent requests
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: No enrolled courses found500: Internal server error
Endpoint: POST /module/update
Description: Records the current page/section user is studying to enable resume functionality.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Request Body:
{
"ModuleName": "Introduction to Python",
"CompletedPageName": "Variables",
"LastseenPageName": "Data Types"
}Response: 201 Created
{
"message": "Progress for module 'Introduction to Python' updated successfully for user 123e4567-e89b-12d3-a456-426614174000"
}What Happens:
- Validates JWT token and extracts userId
- Sends update event to SQS queue with CompletedPageName and LastseenPageName
- Lambda consumer processes event:
- Looks up moduleId from
Moduletable - Upserts record in
UserModuleProgresstable:CompletedPage: TEXT[] array - appends new page if not already presentLastSeenPage: TEXT field - updates to most recent page visitedCompleted: Set to false
- Looks up moduleId from
- Invalidates Redis cache for resume data and in-progress modules
Database Schema:
CompletedPage(TEXT[]): Array of all unique pages user has completedLastSeenPage(TEXT): Most recent page user visited- Logic prevents duplicate pages in CompletedPage array
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: Module not found500: Internal server error
Endpoint: POST /module/complete
Description: Marks module as completed with quiz score and triggers completion notification email.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Request Body:
{
"ModuleName": "Introduction to Python",
"QuizPercentage": 85.5
}Validation Rules:
QuizPercentage: 0-100, requiredModuleName: Required
Response: 201 Created
{
"message": "Module 'Introduction to Python' completed successfully for user 123e4567-e89b-12d3-a456-426614174000"
}What Happens:
- Validates JWT token and extracts userId
- Sends completion event to DB API
- DB API processes event:
- Looks up moduleId from
Moduletable - Updates
UserModuleProgress:Completed= true,CompletedOn= timestamp - Inserts quiz record into
Quiztable with score
- Looks up moduleId from
- Sends notification request to Notification API with ModuleName and QuizPercentage
- Notification API:
- Retrieves user data from DynamoDB
- Fetches "ModuleCompletion" email template
- Formats message with user name, module name, and quiz score
- Publishes to SNS → SQS → Lambda sends email
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: Module not found500: Internal server error
Endpoint: GET /module/resume?moduleName=<module_name>
Description: Retrieves the last page user accessed in a module to continue from where they left off.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Query Parameters:
moduleName(required): Name of the module
Example:
GET /module/resume?moduleName=Introduction%20to%20Python
Response: 200 OK
{
"lastPage": "Data Types"
}Note: Returns the LastSeenPage value, which is the most recent page the user visited, not the CompletedPage array.
What Happens:
- Validates JWT token and extracts userId
- Checks Redis cache for resume data
- If cache miss, sends request to DB API
- DB API queries
UserModuleProgresstable for last saved page - Caches result in Redis
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: Module not found or no progress saved500: Internal server error
Endpoint: GET /module/inProgress
Description: Retrieves all modules the authenticated user is currently studying (started but not completed).
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Response: 200 OK
{
"modules": [
"Introduction to Python",
"Variables and Data Types",
"Control Flow"
],
"courseNames": [
"Python Fundamentals",
"Python Fundamentals",
"Python Fundamentals"
],
"lastPages": [
["intro", "basics", "functions"],
["variables", "types"],
["if-statements"]
]
}Note: lastPages returns TEXT[] arrays containing all unique pages completed for each module.
What Happens:
- Validates JWT token and extracts userId
- Checks Redis cache for in-progress modules
- If cache miss, queries DB API
- DB API joins
Module,UserModuleProgress, andCoursetables - Filters for modules where
Completed= FALSE - Returns module names, associated course names, and last accessed pages
- Caches result in Redis
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: No in-progress modules found500: Internal server error
Endpoint: GET /module/completed
Description: Retrieves the 3 most recently completed modules for the authenticated user.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Response: 200 OK
{
"modules": [
"Introduction to Python",
"Variables and Data Types",
"Functions"
],
"completionTimes": [
"2024-01-15T10:30:00",
"2024-01-14T15:45:00",
"2024-01-13T09:20:00"
]
}What Happens:
- Validates JWT token and extracts userId
- Checks Redis cache for completed modules
- If cache miss, queries DB API
- DB API joins
ModuleandUserModuleProgresstables - Filters for modules where
Completed= TRUE - Orders by
CompletedOnDESC and limits to 3 most recent - Returns module names and completion timestamps in ISO format
- Caches result in Redis
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: No completed modules found500: Internal server error
Endpoint: POST /practicequiz/submit
Description: Submits practice quiz score and updates user's quiz statistics (highest, lowest, attempts).
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Request Body:
{
"moduleName": "Introduction to Python",
"score": 85
}Response: 201 Created
{
"message": "Quiz submitted successfully"
}What Happens:
- Validates JWT token and extracts userId
- Sends submission event to SQS queue
- Lambda consumer processes event:
- Looks up moduleId from
Moduletable - Upserts record in
PracticeQuiztable:- Updates
HighestScoreif current score is higher - Updates
LowestScoreif current score is lower - Increments
Attemptscounter
- Updates
- Looks up moduleId from
- Updates Redis cache with new statistics
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: Module not found500: Internal server error
Endpoint: GET /practicequiz/report?moduleName=<module_name>
Description: Retrieves comprehensive quiz performance statistics for a specific module.
Authentication: Required (JWT Bearer Token)
Request Headers:
Authorization: Bearer <jwt_token>
Query Parameters:
moduleName(required): Name of the module
Example:
GET /practicequiz/report?moduleName=Introduction%20to%20Python
Response: 200 OK
{
"HighestScore": 95,
"LowestScore": 65,
"Attempts": 5
}What Happens:
- Validates JWT token and extracts userId
- Checks Redis cache for report data
- If cache miss, queries DB API
- DB API queries
PracticeQuiztable for user's statistics - Caches result in Redis
Error Responses:
401: Invalid or expired token403: Forbidden - Insufficient permissions404: Module not found or no quiz attempts500: Internal server error
Endpoint: POST /api/v1/user/create
Description: Creates user profile in DynamoDB for notification preferences (called internally by Main API during registration).
Authentication: None (Internal API)
Request Body:
{
"userId": "123e4567-e89b-12d3-a456-426614174000",
"name": "John Doe",
"email": "john.doe@example.com"
}Response: 200 OK
{
"user_id": "123e4567-e89b-12d3-a456-426614174000"
}What Happens:
- Stores user data in DynamoDB
Userstable - Sets default notification channel to "email"
- Prepares user for receiving notifications
Error Responses:
500: DynamoDB write error
Endpoint: POST /notify/
Description: Sends event-based notification to user (registration, module completion, etc.).
Authentication: None (Internal API)
Request Body:
For Registration:
{
"userId": "123e4567-e89b-12d3-a456-426614174000",
"TemplateType": "Registration"
}For Module Completion:
{
"userId": "123e4567-e89b-12d3-a456-426614174000",
"TemplateType": "ModuleCompletion",
"ModuleName": "Introduction to Python",
"QuizPercentage": 85
}For Course Purchase:
{
"userId": "123e4567-e89b-12d3-a456-426614174000",
"TemplateType": "CoursePurchase",
"CourseName": "Python Fundamentals"
}For Account Deletion:
{
"userId": "123e4567-e89b-12d3-a456-426614174000",
"TemplateType": "AccountDeletion"
}Response: 200 OK
{
"message": "Notification sent successfully"
}What Happens:
- Retrieves user data from DynamoDB
Userstable - Validates required fields based on TemplateType:
- ModuleCompletion: Requires
ModuleNameandQuizPercentage - CoursePurchase: Requires
CourseName - AccountDeletion: No additional fields required
- ModuleCompletion: Requires
- Fetches email template from DynamoDB
Templatestable - Formats message by replacing placeholders:
{Name}→ User's name{ModuleName}→ Module name (for ModuleCompletion){QuizPercentage}→ Quiz score (for ModuleCompletion){CourseName}→ Course name (for CoursePurchase)
- Publishes message to SNS topic with channel filter
- SNS routes to appropriate SQS queue (email, apn, fcm)
- Lambda consumer processes queue:
- Email Lambda sends via Gmail SMTP
- Other channels handled by respective consumers
Error Responses:
400: Missing required fields (e.g., QuizPercentage and ModuleName for ModuleCompletion, CourseName for CoursePurchase)404: User or template not found500: SNS publish error or message formatting failure
Endpoint: GET /health
Description: Checks if Nginx reverse proxy is running.
Authentication: None (Public)
Response: 200 OK
{
"status": "healthy"
}Endpoint: GET /health (on Main API container)
Description: Checks if Main API service is running.
Authentication: None (Public)
Response: 200 OK
{
"status": "healthy"
}Endpoint: GET /health (on DB API container)
Description: Checks if DB API service is running.
Authentication: None (Public)
Response: 200 OK
{
"status": "healthy"
}Endpoint: GET /health (on Notification API container)
Description: Checks if Notification API service is running.
Authentication: None (Public)
Response: 200 OK
{
"status": "healthy"
}All endpoints return errors in this format:
{
"detail": "Error message describing what went wrong"
}Common HTTP Status Codes:
200: Success201: Created (for POST endpoints that create resources)400: Bad request (validation error)401: Unauthorized (invalid/expired token)403: Forbidden (authenticated but insufficient permissions)404: Resource not found500: Internal server error
Nginx acts as a reverse proxy and API gateway sitting between the AWS Load Balancer and your backend services. It's the single entry point that routes incoming requests to the appropriate microservice.
Client → AWS ALB (Port 80) → EC2 (Port 80) → Nginx Container (Port 80) → Backend Services
↓
┌─────────────────┼─────────────────┐
↓ ↓ ↓
Main API (8000) DB API (8080) Notification API (8001)
events {
worker_connections 1024;
}Purpose: Configures how Nginx handles connections
worker_connections 1024: Each Nginx worker process can handle up to 1024 simultaneous connections- With default 1 worker, this means 1024 concurrent connections total
upstream main_api {
server main-api:8000;
}
upstream db_api {
server db-api:8080;
}
upstream notification_api {
server notification-api:8001;
}Purpose: Defines backend service groups for load balancing
main-api:8000: Docker service name and port (Docker DNS resolves this)db-api:8080: Database service endpointnotification-api:8001: Notification service endpoint
Why use upstreams?
- Enables load balancing if you scale to multiple containers
- Provides health checking capabilities
- Cleaner configuration with reusable names
server {
listen 80;
...
}Purpose: Nginx listens on port 80 for incoming HTTP requests from the Load Balancer
location /health {
return 200 "healthy\n";
add_header Content-Type text/plain;
}Purpose: ALB Target Group health checks
- ALB periodically sends
GET /healthrequests - Nginx responds with
200 OKif running - If Nginx is down, ALB marks the EC2 instance as unhealthy
Request: http://your-alb.amazonaws.com/health
Response: 200 OK with body "healthy"
Main API Routes:
location /api/main/ {
proxy_pass http://main_api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}What happens:
- Client requests:
http://alb.com/api/main/user/login - Nginx matches
/api/main/prefix - Strips
/api/mainand forwards/user/logintomain-api:8000 - Main API receives:
GET /user/login
Headers explained:
Host: Original hostname from client requestX-Real-IP: Client's actual IP address (not ALB's IP)X-Forwarded-For: Chain of proxy IPs (for tracking request path)
DB API Routes:
location /api/db/ {
proxy_pass http://db_api/;
...
}- Client:
http://alb.com/api/db/user/profile/123 - Forwards to:
db-api:8080/user/profile/123
Notification API Routes:
location /api/notification/ {
proxy_pass http://notification_api/;
...
}- Client:
http://alb.com/api/notification/notify/ - Forwards to:
notification-api:8001/notify/
- One public endpoint for all services
- Simplifies client configuration
- Easier to manage SSL/TLS certificates
- Backend services don't need to be publicly accessible
- Only Nginx container exposes port 80
- Services communicate via Docker internal network
# If you scale to multiple containers:
upstream main_api {
server main-api-1:8000;
server main-api-2:8000;
server main-api-3:8000;
}Nginx automatically distributes requests across instances
- Routes by URL path prefix
- No need for clients to know individual service ports
- Easy to add/remove services without client changes
- Nginx is extremely fast and lightweight
- Handles static content efficiently
- Connection pooling to backend services
- Request buffering
- Hides internal service architecture
- Can add rate limiting
- Can add authentication at gateway level
- Protects against slow HTTP attacks
┌─────────────────────────────────────────────────────────────┐
│ AWS Load Balancer │
│ (Port 80, Public IP) │
└────────────────────────┬────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ EC2 Instance │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Nginx Container (Port 80) │ │
│ │ │ │
│ │ Routes: │ │
│ │ /health → 200 OK │ │
│ │ /api/main/* → main-api:8000 │ │
│ │ /api/db/* → db-api:8080 │ │
│ │ /api/notification/* → notification-api:8001 │ │
│ └───────────┬───────────────┬───────────────┬──────────┘ │
│ │ │ │ │
│ ┌───────────▼─────┐ ┌──────▼──────┐ ┌─────▼──────────┐ │
│ │ Main API │ │ DB API │ │ Notification │ │
│ │ (Port 8000) │ │ (Port 8080) │ │ API (8001) │ │
│ └─────────────────┘ └─────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘
User Login Request:
-
Client sends:
POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/api/main/user/login -
AWS ALB receives:
- Checks target group health
- Forwards to EC2 instance on port 80
-
Nginx receives:
POST /api/main/user/login- Matches
location /api/main/ - Strips
/api/mainprefix - Adds proxy headers
- Matches
-
Main API receives:
POST /user/login Headers: Host: mleraalb-1595386243.ap-south-1.elb.amazonaws.com X-Real-IP: 203.0.113.45 (client's actual IP) X-Forwarded-For: 203.0.113.45 -
Response flows back:
Main API → Nginx → ALB → Client
services:
nginx:
image: nginx:alpine
ports:
- "80:80" # Expose to host
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- app-network
main-api:
# No ports exposed to host!
# Only accessible via Docker network
networks:
- app-networkKey points:
- Only Nginx exposes port 80 to the host
- Backend services are isolated on
app-network - Services communicate using Docker DNS (service names)
- Microservices Pattern: Each service is independent
- Scalability: Can scale services independently
- Security: Backend services not directly accessible
- Flexibility: Easy to add new services or change routing
- Monitoring: Single point to add logging/metrics
- SSL Termination: Can add HTTPS at Nginx level
# Rate limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api/main/ {
limit_req zone=api_limit burst=20;
proxy_pass http://main_api/;
}
# Caching
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m;
location /api/main/static/ {
proxy_cache api_cache;
proxy_cache_valid 200 1h;
proxy_pass http://main_api/static/;
}
# CORS headers
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;Client → Main API → DB API → PostgreSQL
↓
Notification API → DynamoDB
↓
SNS → SQS → Lambda → Gmail SMTP
Client → Main API → DB API → SQS Queue
↓
Lambda Consumer → PostgreSQL
↓
Redis Cache
Client → Main API → DB API → PostgreSQL
↓
Notification API (ModuleName, QuizPercentage)
↓
DynamoDB (User + Template)
↓
SNS → SQS → Lambda → Email
Client → Main API → DB API → PostgreSQL
↓
Notification API (CourseName)
↓
DynamoDB (User + Template)
↓
SNS → SQS → Lambda → Email
Client → Main API → DB API → SQS Queue
↓
Lambda Consumer → PostgreSQL (CASCADE DELETE)
↓
Clears Redis Cache
↓
Notification API (AccountDeletion)
↓
DynamoDB (User + Template)
↓
SNS → SQS → Lambda → Email
User
- UserId (UUID, PK)
- Name (TEXT)
- Profession (TEXT)
- Phone (TEXT)
- Email (TEXT, UNIQUE)
- CreatedOn (TIMESTAMP)
Auth
- Email (TEXT, PK)
- PasswordHash (TEXT)
- UserId (UUID, FK → User)
Course
- CourseId (UUID, PK)
- Type (TEXT)
- CourseName (TEXT, UNIQUE)
Module
- ModuleId (UUID, PK)
- CourseId (UUID, FK → Course)
- ModuleName (TEXT)
UserCourse
- UserId (UUID, PK, FK → User)
- CourseId (UUID, PK, FK → Course)
UserModuleProgress
- UserId (UUID, PK, FK → User)
- ModuleId (UUID, PK, FK → Module)
- CompletedPage (TEXT)
- Completed (BOOLEAN)
- CompletedOn (TIMESTAMP)
Quiz
- QuizId (UUID, PK)
- UserId (UUID, FK → User)
- ModuleId (UUID, FK → Module)
- Percent (INTEGER)
- Pass (BOOLEAN)
- AttemptedOn (TIMESTAMP)
PracticeQuiz
- UserId (UUID, PK, FK → User)
- ModuleId (UUID, PK, FK → Module)
- HighestScore (INTEGER)
- LowestScore (INTEGER)
- Attempts (INTEGER)
Users
- userId (String, PK)
- channel (String, SK)
- id (String) - email address
- Name (String)
Templates
- TemplateType (String, PK)
- Channel (String, SK)
- TemplateId (String)
- Subject (String)
- Body (String)
- Version (Number)
- CreatedAt (String)
-
Connection Pooling:
- PostgreSQL: 1-10 connections
- Redis: 10 max connections
-
Caching Strategy:
- User profiles cached in Redis
- Course progress cached in Redis
- Module resume data cached in Redis
- Quiz reports cached in Redis
-
Async Processing:
- Course purchases processed via SQS
- Module updates processed via SQS
- Notifications sent via SNS → SQS → Lambda
- Password Security: Bcrypt hashing with salt
- JWT Tokens: 1-hour expiration, HS256 algorithm
- CORS: Configured for specific origins (update in production)
- Environment Variables: Sensitive data stored in .env
- Database: Connection pooling prevents exhaustion
- API Gateway: Nginx reverse proxy for routing
Register User:
curl -X POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/user/register \
-H "Content-Type: application/json" \
-d '{
"Name": "John Doe",
"Phone": "9876543210",
"Email": "john.doe@example.com",
"Profession": "Software Engineer",
"Password": "securePassword123"
}'Login:
curl -X POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/user/login \
-H "Content-Type: application/json" \
-d '{
"Email": "john.doe@example.com",
"Password": "securePassword123"
}'Get Profile (with token):
curl -X GET http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/user/profile \
-H "Authorization: Bearer <your_jwt_token>"Purchase Course:
curl -X POST http://mleraalb-1595386243.ap-south-1.elb.amazonaws.com/course/purchase \
-H "Authorization: Bearer <your_jwt_token>" \
-H "Content-Type: application/json" \
-d '{
"courseName": "Python Fundamentals"
}'Common Issues:
-
401 Unauthorized: Token expired or invalid
- Solution: Login again to get new token
-
404 Not Found: Resource doesn't exist
- Solution: Verify course/module names are correct
-
500 Internal Server Error: Server-side issue
- Solution: Check logs with
docker-compose logs -f
- Solution: Check logs with
-
Connection Timeout: Network/security group issue
- Solution: Verify ALB security group allows port 80
Logs:
# View all logs
docker-compose logs -f
# View specific service
docker-compose logs -f main-api
docker-compose logs -f db-api
docker-compose logs -f notification-api- v1.0.0 (2024): Initial release with core functionality
- User management
- Course enrollment
- Module progress tracking
- Practice quizzes
- Email notifications
For issues or questions, check:
- GitHub Issues: [Repository URL]
- Documentation: This file
- Deployment Guide: DEPLOYMENT.md