This project is a full-stack application organized in a monorepo structure. It contains the following major components:
- Frontend: React/React Native/Next.js (as applicable), written in TypeScript, bundled and deployed via AWS Amplify.
- Backend: Spring Boot (Java 17) API hosted on AWS Elastic Beanstalk. Provides CRUD endpoints for the application.
- Database: Amazon RDS (MySQL) as the persistent storage layer.
- Frontend: React Native / React (TypeScript), Expo, Amplify hosting
- Backend: Spring Boot (Java 17), Maven build
- Database: Amazon RDS (MySQL engine)
- Infrastructure: AWS Amplify, AWS Elastic Beanstalk, AWS RDS, VPC, Security Groups
- CI/CD: GitHub Actions (staging branches
staging-frontendandstaging-backend)
The diagram shows:
- Amplify (Frontend) → communicates over HTTPS
- CloudFront → terminates HTTPS and forwards requests to Elastic Beanstalk over HTTP
- Elastic Beanstalk (Spring Boot API) → handles backend logic and connects to RDS
- RDS (MySQL) → backend data layer, typically accessed via JDBC (SSL optional)
- Navigate to the
frontendfolder of the monorepo. - Install dependencies:
npm install
- Run the development server locally:
npm run dev
- Build the production bundle:
npm run build
- Test endpoints by pointing API URLs to your local backend or deployed backend (
.env).
- Navigate to the
backendfolder of the monorepo.cd backend - Verify Maven wrapper permissions (No need on Windows):
chmod +x mvnw
- Build the backend (see Windows Alternative Commands):
./mvnw package -DskipTests=true
- Run locally with a connected RDS instance:
./mvnw spring-boot:run
- Update
application.propertiesorapplication.ymlwith RDS connection details:spring.datasource.url=jdbc:mysql://<rds-endpoint>:3306/<dbname> spring.datasource.username=admin spring.datasource.password=<password> spring.jpa.hibernate.ddl-auto=update
If you are on Windows, use these commands instead of the Linux/Mac commands:
-
Navigate to the
backendfolder:cd backend
-
Skip
chmod +x(not required on Windows). -
Build the backend:
mvnw.cmd package -DskipTests=true
-
Run locally with a connected RDS instance:
mvnw.cmd spring-boot:run
- Log in to AWS Management Console → Search RDS → Create database.
- Choose Standard Create, MySQL, Free-tier if available.
- Database instance identifier:
project-db - Credentials:
- Master username:
admin - Password: generate and save securely.
- Master username:
- Instance size:
db.t3.micro(free-tier) - VPC: Use default VPC or create a dedicated VPC.
- Public Access: Set to Yes if you want external Workbench access (otherwise use private).
- Go to EC2 → Security Groups.
- Locate the RDS security group, Edit inbound rules:
- Add MySQL/Aurora rule: Port 3306, Source your IP (or Elastic Beanstalk security group).
- From local machine:
Use MySQL Workbench:Hostname: <rds-endpoint> Port: 3306 Username: admin Password: <password> - Ensure inbound rules allow your current IP.
- Open AWS Console → Elastic Beanstalk → Create Application.
- Platform: Java 17, Upload source: skip initial upload (we'll deploy via GitHub).
- Set environment name:
movies-backend-env. - Attach to the same VPC and security group used for RDS.
Add .github/workflows/deploy-backend.yml:
name: Deploy to Backend
on:
push:
branches:
- staging-backend
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for Maven wrapper
working-directory: ./backend
run: chmod +x mvnw
- name: Build Spring Boot Jar
working-directory: ./backend
run: ./mvnw package -DskipTests=true
- name: Deploy to Elastic Beanstalk
uses: einaregilsson/beanstalk-deploy@v21
with:
application_name: "movies-backend"
environment_name: "movies-backend-env"
region: "us-east-2"
version_label: ${{ github.sha }}
aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
file: ./backend/target/*.jar- In GitHub Repo → Settings → Secrets and Variables:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
- Initial manual deploy through EB Console → Upload and deploy.
- Subsequent pushes to
staging-backendbranch trigger automatic deploy.
Because the backend Elastic Beanstalk environment uses HTTP by default, browsers block requests from the frontend (Amplify on HTTPS) due to mixed-content restrictions. To solve this, we configured Amazon CloudFront as an HTTPS reverse proxy in front of Elastic Beanstalk.
-
Create a CloudFront Distribution
- Go to AWS Console → CloudFront → Create Distribution.
- Origin domain: Select your Elastic Beanstalk endpoint (e.g.,
your-backend-env.elasticbeanstalk.com). - Set Origin Protocol Policy =
HTTP Only. - Set Viewer Protocol Policy =
Redirect HTTP to HTTPS.
-
Alternate Domain (optional)
- If using a custom domain, add it under "Alternate Domain Names (CNAMEs)" and configure DNS.
-
Default Cache Behavior
- Allowed HTTP Methods:
GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE. - Disable caching for API paths.
- Allowed HTTP Methods:
-
SSL/TLS
- Choose an AWS-managed SSL certificate.
-
Deploy
- Save and deploy the distribution. You will get a CloudFront URL like:
https://<your-distribution>.cloudfront.net
- Save and deploy the distribution. You will get a CloudFront URL like:
-
Update Frontend API URL
- Change
NEXT_PUBLIC_API_URLin the frontend.envto use the CloudFront URL.
- Change
-
CORS
- Ensure the backend CORS configuration allows both the Amplify and CloudFront domains.
- AWS Console → Amplify → Create New App → Host web app.
- Choose GitHub as the repository, select the monorepo root.
- Build settings: Specify frontend folder (Amplify detects automatically or edit
amplify.yml).
-
Add
.envkeys (e.g.,API_URLpointing to Elastic Beanstalk API endpoint).
- Pushes to the
staging-frontendbranch trigger automatic build and deploy in Amplify.
- Frontend:
staging-frontend→ AWS Amplify. - Backend:
staging-backend→ AWS Elastic Beanstalk.
- Backend ↔ RDS: EB app connects to RDS endpoint in the same VPC/security group.
- Frontend ↔ Backend: Amplify app uses the Elastic Beanstalk API endpoint (e.g.,
https://<cloudfront>/api). - Authentication: API secured with CORS, only frontend Amplify domain allowed.
To secure API calls, the backend enforces CORS so that only requests from trusted domains are allowed. This is controlled by the ALLOWED_ORIGIN environment variable.
Example in application.properties or configuration class:
ALLOWED_ORIGIN=https://your-frontend.amplifyapp.comSpring Boot CORS filter sample:
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(System.getenv("ALLOWED_ORIGIN"))
.allowedMethods("*");
}
};
}- Developer commits code and pushes to the respective
staging-*branch. - GitHub Actions (backend) or Amplify CI/CD (frontend) builds and deploys automatically.
- On success, updated environment is live.
Base URL: https://<cloudfront-or-elastic-beanstalk-url>/api/movies
GET /api/moviesResponse:
[
{
"id": 1,
"title": "Movie Title",
"releaseYear": 2024,
"genre": "Action",
"posterUrl": "http://...",
"description": "Movie description"
}
]GET /api/movies/{id}POST /api/movies
Content-Type: application/jsonBody:
{
"title": "New Movie",
"releaseYear": 2024,
"genre": "Drama",
"posterUrl": "http://...",
"description": "Description"
}PUT /api/movies/{id}
Content-Type: application/jsonBody:
{
"title": "Updated Movie",
"releaseYear": 2025,
"genre": "Comedy",
"posterUrl": "http://...",
"description": "Updated Description"
}DELETE /api/movies/{id}- Use Postman or cURL to test CRUD operations.
- Ensure correct CORS configuration in the backend to allow frontend domain.
When deploying the backend to Elastic Beanstalk, set the following environment properties under:
Elastic Beanstalk → Configuration → Software → Environment properties.
DB_URL=<your-rds-endpoint>DB_USERNAME=<db-username>DB_PASSWORD=<db-password>DB_PORT=3306DB_MOVIETABLE=<database-name>SPRING_PROFILES_ACTIVE=dev(or other profile as needed)ALLOWED_ORIGIN=<frontend-amplify-domain>(to allow CORS)
Example:
DB_URL=your-rds-endpoint.rds.amazonaws.com
DB_USERNAME=your-db-username
DB_PASSWORD=your-db-password
DB_PORT=3306
DB_MOVIETABLE=movies_db
SPRING_PROFILES_ACTIVE=dev
ALLOWED_ORIGIN=https://your-frontend.amplifyapp.comThese variables are automatically injected into Spring Boot at runtime.
For local development or when configuring Amplify:
NEXT_PUBLIC_API_URL=http://localhost:8080When deployed on Amplify, NEXT_PUBLIC_API_URL should point to the backend's public URL (Elastic Beanstalk or CloudFront):
NEXT_PUBLIC_API_URL=https://<your-cloudfront-distribution>.cloudfront.netNote: The
NEXT_PUBLIC_prefix is required in Next.js/React so that variables are exposed to the browser.
This diagram illustrates the network architecture, including Virtual Private Cloud (VPC), subnets, gateways, EC2, RDS, and image storage components
The VPC isolates our backend (Elastic Beanstalk EC2 instances) and database (RDS) from the public internet. This provides network-level control over subnets, routing, and security groups. Only Elastic Beanstalk can reach RDS directly, and external clients never touch the database.
The Internet Gateway allows inbound/outbound traffic for resources inside our VPC, such as Beanstalk EC2 instances.
The NAT Gateway allows private resources like RDS to initiate outbound requests (e.g., OS or patch updates) without being directly exposed.
Note: Amplify and CloudFront are AWS-managed services outside the VPC, so they do not depend on our IGW/NAT.
- RDS is in a private subnet with no public exposure.
- CORS is configured so only our Amplify frontend can call the backend API.
- Secrets such as DB credentials, API keys, and JWT secrets are stored in AWS Systems Manager Parameter Store and Amplify environment variables, not hardcoded.
Elastic Beanstalk abstracts away provisioning, scaling, and load balancing. It still runs on EC2, but Beanstalk manages patching, auto-scaling, and deployments. This saved us from manual configuration.
CloudFront wasn’t just for caching. Our Beanstalk backend served only HTTP, so CloudFront terminated HTTPS and ensured secure traffic between frontend and backend.
We needed a relational database for structured queries and entity relationships (movies, users). MySQL fit the free tier, worked well with Spring Boot, and was simpler than DynamoDB or Aurora for this project.
Separate pipelines for frontend and backend allowed independent release cycles. GitHub Actions automated builds, tests, and deployments, ensuring production parity and reducing manual effort.
Amplify is linked to our GitHub repo and triggers builds when code is pushed to a specific branch. The amplify.yml defines install, build, and artifact steps.
On pushes to staging-backend, GitHub Actions checks out the repo, sets up Java 17, builds with Maven, and deploys the JAR to Elastic Beanstalk using the AWS CLI.
Currently, poster URLs are fetched from OMDb and stored as links. In production, S3 would ensure reliability and prevent broken links.
Firebase Authentication was quick to implement and matched our team’s skills and timeline. AWS Cognito would be a strong alternative in a production environment.
Multi-AZ was disabled to stay within free tier. In production, we would enable Multi-AZ and automated backups for higher availability.






