Skip to content

Development Workflow

GPXSheet has two backend dev modes and one frontend dev server. Mix and match depending on what you're working on.


Quick reference

Goal Commands
Edit frontend only make dev-api + make dev-ui
Edit frontend + backend make infra + make dev-api-full + make dev-worker + make dev-ui
Edit backend only (no UI) make dev-api (or dev-api-full for real queue)
Verify the full built product make frontend then make dev-api or docker compose up --build

Mode 1 — Simple (EagerRunner, no deps)

make dev-api    # uvicorn --reload on :8000
make dev-ui     # Vite on :5173, proxies /v1/ to :8000

How it works: The API runs in "dev path" — no Redis or MinIO. Jobs execute synchronously in the same process (EagerRunner) and results are stored on the local filesystem under /tmp/gpxsheet-results. The POST /v1/render response comes back with status: done immediately (no polling needed).

What it's good for: - Frontend iteration — form interactions, layout, polling UI - Python logic changes (analysis, strip renderer, PDF layout) - Running the test suite

What it doesn't test: - Real async job queue behavior (Dramatiq worker picking up jobs) - MinIO result storage and presigned URL redirects (303 responses) - Multi-process separation (API and worker are one process here)

Prerequisites: just Python + .venv.


Mode 2 — Full stack (Redis + MinIO in Docker, Python on host)

# One-time: start infrastructure containers
make infra

# Three terminals:
make dev-api-full   # uvicorn --reload on :8000, prod path
make dev-worker     # Dramatiq worker
make dev-ui         # Vite on :5173

How it works: make infra starts Redis and MinIO in Docker (docker-compose.infra.yml). dev-api-full and dev-worker source .env.dev which points them at the containers, activating the prod code path (Dramatiq job queue, MinIO result storage). Python processes run on the host for --reload and debugger access.

What it's good for: - Testing the full async flow: submit → queued → worker picks up → done - Testing MinIO presigned URL redirects (the GET …/result 303 response) - Verifying job deduplication / caching - Frontend polish with realistic latency

What changes without rebuild: - Python source (--reload on the API; restart dev-worker manually) - Frontend source (Vite HMR, instant)

Prerequisites: Docker Desktop running.

make infra-down     # stop containers when done

Ports (all loopback-only): - API: http://localhost:8000 (and http://localhost:5173 via Vite proxy) - MinIO S3: localhost:9000 - MinIO console: http://localhost:9001 (devkey / devsecret1) - Redis: localhost:6379


Mode 3 — Full prod stack in Docker

docker compose up --build

Runs api + worker + redis + minio all in containers (docker-compose.yml). Used to verify the complete built product, including the compiled frontend. The frontend must be built first (make frontend) to appear at /; otherwise the API serves Swagger at /docs.

This is the closest to a production deployment. Python changes require docker compose up --build (no hot reload).


Frontend build

The SPA in frontend/ is gitignored when builtsrc/gpxsheet/service/static/ is excluded from version control. Build it explicitly:

make frontend       # npm ci && npm run build → service/static/

The FastAPI app serves the built files at / via a StaticFiles mount. Without the build, GET / falls through to Swagger UI at /docs. The make dev-ui workflow (Vite dev server) does not need a build — it serves the SPA directly from source with HMR.


Environment files

.env.dev — committed, dev-only throwaway credentials for docker-compose.infra.yml. Used automatically by make dev-api-full and make dev-worker (sourced in a subshell, not exported to your shell session).

The prod docker-compose.yml uses separate minioadmin defaults which are blocked by _guard_prod_secrets() in app.py — those credentials are only valid inside the Docker network where you know what you're doing.


Switching between modes

Modes 1 and 2 share port 8000. You can't run both at once. Stop one before starting the other. Mode 3 (docker compose up) also uses port 8000; stop dev-api-full first.

The Vite dev server (make dev-ui) works with any mode — it just proxies /v1/ to whatever is on port 8000.