One-command local stack using Docker Compose with the dev profile.
- Docker Desktop (or Docker Engine + Compose plugin)
- Git
- For contract development: Rust stable with wasm32 target
rustup install stable rustup target add wasm32-unknown-unknown
cp .env.dev.example .env.devOpen .env.dev and fill in any values marked as required. The defaults work
out of the box for local development — at minimum set a real JWT_SECRET:
# Generate a strong secret
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"docker compose -f docker-compose.dev.yml --profile dev upThis starts:
- PostgreSQL on
localhost:5432 - Backend (NestJS) on
localhost:3001with hot reload
Add --build to force a rebuild of the backend image (e.g. after changing
Dockerfile.dev or package.json):
docker compose -f docker-compose.dev.yml --profile dev up --buildcurl http://localhost:3001/v1/health
# Expected: {"status":"ok","timestamp":"..."}Or open http://localhost:3001/v1/health in your browser.
All frontend API clients resolve the backend origin through the shared
getApiBaseUrl() helper in frontend/src/lib/api/base-url.ts, instead of
each module hardcoding its own localhost host/port fallback.
- Set
NEXT_PUBLIC_API_URLto override the backend origin, e.g. infrontend/.env.local:NEXT_PUBLIC_API_URL=http://localhost:3001
- When unset, it defaults to
http://localhost:3001(matching the backend'sdocker-compose.yml/docker-compose.dev.ymlport), so local dev works out of the box without any frontend env file. NEXT_PUBLIC_API_URLshould be the bare origin (protocol + host + optional port) — individual clients append their own resource paths (e.g./v1/...,/api/v1/...,/favorites) on top of it. Do not include a trailing slash.- If you add a new frontend module that calls the backend, import
getApiBaseUrl()(orgetConfiguredApiBaseUrl()if you need a different fallback than the shared absolute default, e.g. a same-origin relative URL) from@/lib/api/base-urlrather than readingprocess.env.NEXT_PUBLIC_API_URLdirectly.
Contracts are in the contract/ directory and use Soroban SDK.
Test contract code before committing:
cd contract
# Run all tests
cargo test --all-features
# Run tests for a specific contract
cd contract/contracts/myfans-token && cargo test
# Run with output for debugging
cargo test -- --nocapture
# Watch mode (requires cargo-watch: cargo install cargo-watch)
cargo watch -x testBefore pushing contract changes, run all CI checks locally:
cd contract && \
cargo fmt --all --check && \
cargo clippy --all-targets --all-features -- -D warnings && \
cargo test --all-features && \
cargo build --release --target wasm32-unknown-unknownTo build optimized WASM files for deployment:
cd contract
cargo build --release --target wasm32-unknown-unknownArtifacts appear in: contract/target/wasm32-unknown-unknown/release/
For comprehensive testing patterns, see contract/TESTING.md
Key points:
- All tests use the Soroban test environment (no network access required)
- Tests cover happy path, error conditions, and edge cases
- Authorization and cross-contract interactions are tested
- Every PR requires passing contract tests in CI
Use the Regression Prevention Checklist when:
- Adding new contracts
- Modifying contract interfaces
- Adding new contract methods
- Changing authorization rules
The backend source (./backend/src) is bind-mounted into the container.
nest start --watch watches for .ts file changes and recompiles automatically.
To see the watcher output:
docker compose -f docker-compose.dev.yml --profile dev logs -f backendEdit any file under backend/src/, save it, and watch the logs — the NestJS
process will restart within a few seconds and your change will be live.
To wipe the dev database and start fresh (removes the postgres_dev_data volume):
docker compose -f docker-compose.dev.yml --profile dev down -v
docker compose -f docker-compose.dev.yml --profile dev upNote: This only removes the dev volume (
myfans_postgres_dev_data). Production data is unaffected.
# Stop containers (keep volumes)
docker compose -f docker-compose.dev.yml --profile dev down
# Stop containers and remove volumes (full reset)
docker compose -f docker-compose.dev.yml --profile dev down -vPort 5432 already in use
Another Postgres instance is running locally. Stop it or change the host port
in docker-compose.dev.yml.
Backend container is unhealthy
Check the logs:
docker compose -f docker-compose.dev.yml --profile dev logs backendCommon causes: missing JWT_SECRET in .env.dev, TypeORM migration failure,
or Postgres not yet ready.
Changes not reflected after editing a file Confirm the watcher is running in the logs. If the container exited, restart it:
docker compose -f docker-compose.dev.yml --profile dev restart backendStale image after changing Dockerfile.dev or package.json
Force a rebuild:
docker compose -f docker-compose.dev.yml --profile dev up --buildContract tests failing locally but passing in CI Ensure you're using the same workspace manifest:
cd contract && cargo test --all-features --manifest-path Cargo.tomlRust toolchain not found Install Rust and add the wasm32 target:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-unknown-unknown