Skip to content

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

mecca register git@gitlab.com:appmecca-sample-apps/spring-react-fullstack.git \
  -e dev

2. Build the containers

mecca build trigger \
  -e dev \
  -g git@gitlab.com:appmecca-sample-apps/spring-react-fullstack.git
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

mecca app deploy -e dev spring-react-fullstack
Expected deploy output
Deployment queued: spring-react-fullstack
Configuring routing for backend...
Publishing static bundle for frontend...
Starting containers...
Health checks passing.
Deployment completed successfully.

4. Verify

mecca app show -e dev spring-react-fullstack
# 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