5.4 KiB
Tutorial — Local development and live reload
This guide explains how to work on SFusion day to day, and what kind of live reload / hot reload you can expect (and what you cannot).
1. What actually reloads?
| Part of the stack | Live / hot reload | Notes |
|---|---|---|
React + Vite (frontend/) |
Yes — HMR | Vite pushes updated modules to the browser; state is preserved where React Fast Refresh allows it. |
Vapor backend (backend/) |
No true hot reload | Swift compiles to a native binary. Any change to server code requires rebuild + process restart. You can automate restarts with file watchers (below). |
| PostgreSQL schema | No | Fluent runs migrations when the app starts (AUTO_MIGRATE / dev). Schema changes are deliberate migration steps, not hot-patched. |
So: frontend feels like live reload out of the box; backend is save → rebuild → rerun, optionally scripted.
2. Recommended setup for the best DX
Run three processes on the host (not inside the backend container), and only use Docker for Postgres if you want:
- PostgreSQL —
docker compose up postgresor a local Postgres listening on5432. - Backend —
swift run Runfrombackend/, withDATABASE_*/AUTO_MIGRATEset. - Frontend —
npm run devfromfrontend/, with Vite proxying/apito the backend.
That way you get instant HMR on the UI and incremental Swift builds between short restarts.
3. Database
Option A — Postgres in Docker
docker compose up postgres
Use the same credentials as in docker-compose.yml (defaults: user/database/password sfusion) and point the backend at localhost:5432.
Option B — Postgres installed locally
Create database and users to match your DATABASE_* environment variables.
4. Backend (Vapor / Swift)
From backend/:
export DATABASE_HOST=localhost
export DATABASE_PORT=5432
export DATABASE_USERNAME=sfusion
export DATABASE_PASSWORD=sfusion
export DATABASE_NAME=sfusion
export AUTO_MIGRATE=true
swift run Run
The server listens on 0.0.0.0:8080 by default (configure.swift).
“Live reload” for Swift means auto-restart on save
There is no Swift equivalent of Vite’s HMR at the HTTP layer. Practical approach: watch Sources/**/*.swift and Package.swift, then build and run again.
entr (e.g. brew install entr):
With DATABASE_* and AUTO_MIGRATE exported in your shell beforehand:
cd backend
(find Sources -name '*.swift'; echo Package.swift) | entr -r swift run Run
swift run performs an incremental build when needed and then launches Run; entr -r restarts that whole step after each tracked file change.
Note: SPM may place binaries under .build/*/debug/ depending on triple; swift run always runs the right product.
watchexec is a good cross-platform variant:
cd backend
watchexec -e swift -w Sources -w Package.swift --restart -- swift run Run
(Export the same DATABASE_* and AUTO_MIGRATE in your shell.)
IDE
Opening backend/Package.swift in Xcode (or similar) gives incremental builds plus one-click Run; restarting the debugger is still the “reload.”
5. Frontend — real hot module replacement
From frontend/:
npm install
export VITE_API_PROXY_TARGET=http://127.0.0.1:8080
npm run dev
Open http://localhost:5173. Edits under frontend/src update in the browser via Vite HMR.
vite.config.ts proxies /api to VITE_API_PROXY_TARGET; keep that URL pointed at whichever host runs Vapor (127.0.0.1:8080 when the backend runs on your machine).
6. All-in-docker vs hybrid
Full docker compose up
When the frontend service bind-mounts ./frontend, npm run dev in the container still supports Vite HMR: the browser talks to the mapped port (5173), and edits on disk sync through the volume.
Changing Swift in backend/ requires rebuilding the backend image (e.g. docker compose build backend or up --build). There is no hot reload inside the Swift container unless you add your own watcher and dev image.
Hybrid (recommended for backend-heavy changes)
| Service | Where to run |
|---|---|
| Postgres | Docker Compose or native |
| Backend | cd backend && swift run Run (optionally entr/watchexec) |
| Frontend | cd frontend && npm run dev with proxy → http://127.0.0.1:8080 |
7. Environment quick reference
| Variable | Typical local value |
|---|---|
DATABASE_HOST |
localhost |
DATABASE_PORT |
5432 |
DATABASE_USERNAME / DATABASE_PASSWORD / DATABASE_NAME |
sfusion (match docker-compose.yml) |
AUTO_MIGRATE |
true in development |
VITE_API_PROXY_TARGET |
http://127.0.0.1:8080 |
PORT |
8080 (backend) |
Inside Docker’s backend service, DATABASE_HOST is postgres; on the Mac host it must be localhost.
8. Summary
- Treat frontend development as Vite HMR.
- Treat backend development as recompile + rerun, optionally wrapped with
entrorwatchexec. - For the fastest iteration across layers, prefer Docker or local Postgres + local Swift + local Vite over rebuilding the Swift image on every tweak.