Spring + React Fullstack¶
Full-stack todo application with a Java/Spring Boot API and React/Vite frontend.
Overview¶
| Tech Stack | Java 21 / Spring Boot (backend) + React / Vite (frontend) |
| Services | 2 (backend + frontend) |
| Database | PostgreSQL |
| Pattern | Multi-container, build args |
The appmecca.yaml¶
containers:
- name: backend
dockerfile: backend/Dockerfile
context: backend/
deploy:
- name: backend
container: backend
port: 8080
path: /api
health_check:
path: /api/health
startup_timeout: 300
resources:
memory: "512m"
- name: frontend
primary: true
type: static
static_site:
build_command: cd frontend && npm install --no-audit --no-fund && npm run build
output_dir: frontend/dist
spa_fallback: true
index_document: index.html
error_document: index.html
environment:
VITE_API_URL: ${service:backend}
database:
enabled: true
Key Concepts¶
The frontend is type: static — no container¶
The React bundle is built by static_site.build_command and uploaded to
AppMecca object storage, then served through the AppMecca edge cache. There is
no frontend image, no containers[] entry and no args block:
VITE_API_URL is resolved during the build from the environment map on the
static service.
A type: static service must not carry container-shaped fields. Declaring any
of port, replicas above 1, drives, command or health_check on it is a
validation error — there is no container for them to apply to.
Inter-Service URL Resolution¶
The ${service:backend} template in the frontend's environment resolves to the backend's public HTTPS URL (e.g., https://spring-react-fullstack-backend.dev.yourdomain.io).
Multi-Stage Builds¶
The backend uses a multi-stage Docker build: Gradle compiles the Spring Boot JAR in a build stage, then a slim JRE image runs it. The frontend has no Dockerfile at all — see above.
The backend is a JVM service¶
resources.memory: "512m" and health_check.startup_timeout: 300 are both
load-bearing for Spring Boot. The platform defaults — 128 MB and 120 seconds —
are too small for a JVM: the container is OOM-killed during startup (exit code
137), or the deploy runs out of startup budget and is rolled back. The memory
value requires a unit suffix; a bare 512 means 512 bytes.
Step-by-Step Deployment¶
1. Register the app¶
2. Build the containers¶
Expected build output
Build started: spring-react-fullstack
Building container: backend (backend/Dockerfile)
Building static bundle: frontend -> object storage + edge cache
Build completed successfully.
Images pushed:
- spring-react-fullstack/backend:abc1234
Only the backend produces an image. The frontend is a static bundle, so it is uploaded rather than pushed to the registry.
3. Deploy¶
Expected deploy output
4. Verify¶
# Check backend health
curl https://spring-react-fullstack-backend.dev.yourdomain.io/api/health
# Visit frontend
open https://spring-react-fullstack-frontend.dev.yourdomain.io
What's Next¶
- Flask + Vue Fullstack -- Python version of the same pattern
- Express + Svelte Fullstack -- Node.js + SvelteKit with SPA config injection
- Multi-Container Builds -- How multi-container builds work