A full-stack web application for consuming, storing, and displaying MLB statistics. The application pulls data from the official MLB Stats API and presents it through a modern React interface.
βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ
β β β β β β
β React Frontend ββββββΆβ Spring Boot ββββββΆβ PostgreSQL β
β (TypeScript) β β REST API β β Database β
β β β β β β
βββββββββββββββββββ ββββββββββ¬βββββββββ βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β β
β MLB Stats API β
β (External) β
β β
βββββββββββββββββββ
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Vite |
| Backend | Java 21, Spring Boot 3.x |
| Database | PostgreSQL 16 |
| Auth | Google OAuth 2.0 |
| Migrations | Flyway |
| Build | Maven (backend), npm (frontend) |
- Java 21+
- Node.js 18+
- Docker and Docker Compose
- Maven 3.9+ (or use included wrapper)
- Google Cloud Console account (for OAuth)
- Go to Google Cloud Console
- Create a new project (or select existing)
- Navigate to APIs & Services > Credentials
- Click Create Credentials > OAuth 2.0 Client ID
- Select Web application
- Add authorized redirect URI:
http://localhost:8080/login/oauth2/code/google - Save your Client ID and Client Secret
Create a .env file in the project root (for local dev):
export GOOGLE_CLIENT_ID=your-client-id-here
export GOOGLE_CLIENT_SECRET=your-client-secret-hereThen source it: source .env
docker-compose up -dcd backend
SPRING_PROFILES_ACTIVE=dev ./mvnw spring-boot:runThe dev profile enables:
- Debug logging
- Swagger UI at http://localhost:8080/swagger-ui.html
- Disabled rate limiting
cd frontend
npm install
npm run devOpen http://localhost:5173 - you'll be redirected to Google login.
After logging in, trigger a data sync (requires INGESTION_API_KEY in production, but not in dev mode):
curl -X POST http://localhost:8080/api/ingestion/full-sync- GitHub repository with this code
- Digital Ocean account
- Google OAuth credentials configured for your production domain
- Go to Digital Ocean App Platform
- Click Create App
- Connect your GitHub repository
- DO will detect the
.do/app.yamlspec file - Configure the following environment variables (marked as secrets):
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID |
OAuth client ID |
GOOGLE_CLIENT_SECRET |
OAuth client secret |
INGESTION_API_KEY |
Secret key to protect ingestion endpoints |
- Update Google OAuth redirect URI to:
https://your-app.ondigitalocean.app/login/oauth2/code/google - Deploy
After deployment, trigger data sync with your API key:
curl -X POST https://your-app.ondigitalocean.app/api/ingestion/full-sync \
-H "X-API-Key: your-ingestion-api-key"| Component | Size | Monthly Cost |
|---|---|---|
| App Service | basic-xxs (512MB) | ~$5 |
| Managed PostgreSQL | db-s-1vcpu-1gb | ~$15 |
| Total | ~$20/month |
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | localhost | JDBC PostgreSQL URL |
DATABASE_USERNAME |
Yes | mlbstats | Database username |
DATABASE_PASSWORD |
Yes | mlbstats | Database password |
GOOGLE_CLIENT_ID |
Yes | - | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Yes | - | Google OAuth client secret |
INGESTION_API_KEY |
No | - | API key for ingestion endpoints |
INGESTION_ENABLED |
No | true | Enable/disable ingestion endpoints |
RATE_LIMIT_ENABLED |
No | true | Enable rate limiting |
RATE_LIMIT_RPM |
No | 60 | Requests per minute per IP |
SWAGGER_ENABLED |
No | false | Enable Swagger UI |
LOG_LEVEL |
No | INFO | Logging level for com.mlbstats |
PORT |
No | 8080 | Server port |
GET /api/teams- List all teamsGET /api/teams/{id}- Team detailsGET /api/teams/{id}/roster?season=- Team rosterGET /api/teams/{id}/games?season=- Team games
GET /api/players?search=&page=&size=- Search playersGET /api/players/{id}- Player detailsGET /api/players/{id}/batting-stats?season=- Batting statsGET /api/players/{id}/pitching-stats?season=- Pitching statsGET /api/players/leaders/home-runs?season=&limit=- HR leaders
GET /api/games?date=&season=&teamId=- List gamesGET /api/games/{id}- Game detailsGET /api/games/today- Today's games
POST /api/ingestion/full-sync?season=- Full data syncPOST /api/ingestion/teams- Sync teamsPOST /api/ingestion/rosters?season=- Sync rostersPOST /api/ingestion/games?season=- Sync gamesPOST /api/ingestion/stats?season=- Sync statistics
- Google OAuth 2.0 - All users must authenticate via Google
- CSRF Protection - Cookie-based CSRF tokens
- Rate Limiting - 60 requests/minute per IP (configurable)
- API Key Protection - Ingestion endpoints require API key in production
- Non-root Container - Docker runs as unprivileged user
cd backend
# Run tests
./mvnw test
# Build JAR
./mvnw package
# Run with dev profile
SPRING_PROFILES_ACTIVE=dev ./mvnw spring-boot:runcd frontend
# Development server
npm run dev
# Type checking
npm run lint
# Production build
npm run build# Build the combined image
docker build -t mlb-stats .
# Run locally
docker run -p 8080:8080 \
-e DATABASE_URL=jdbc:postgresql://host.docker.internal:5432/mlbstats \
-e DATABASE_USERNAME=mlbstats \
-e DATABASE_PASSWORD=mlbstats \
-e GOOGLE_CLIENT_ID=your-id \
-e GOOGLE_CLIENT_SECRET=your-secret \
mlb-statsMIT