Polyglot monorepo starter: FastAPI + Next.js + Rust, with JWT auth, audit logging, async SQLAlchemy, Celery workers, Docker Compose, Prometheus, and Grafana dashboards. Rust can run as a standalone service or be compiled into Python extensions (PyO3) and WASM modules (wasm-bindgen) for direct import.
- Backend (FastAPI): Auth & audit routes, async SQLAlchemy + Alembic, Redis-backed rate limiting, Prometheus metrics, structured logging, Celery worker/beat scaffolding.
- Frontend (Next.js + TypeScript): Auth flows (login/register/profile), protected dashboard shell, typed API client, Tailwind styles.
- Rust: Binary service scaffold (
apps/rust-svc), PyO3 extension stub (packages/py-core), wasm-bindgen stub (packages/wasm-core), shared TS types (packages/ts-shared). - Agent scaffold: Stub LLM provider with
/api/v1/agents/runendpoint and frontend demo page to submit prompts. - Ops: Docker Compose for Postgres, Redis, backend, frontend, Prometheus, Grafana; health/readiness endpoints; Makefile helpers; Moon task runner for cross-language cached builds.
The API client refreshes an expired access token when an authenticated request returns 401. Concurrent requests share one in-flight refresh; each original request is retried at most once with the new token. Login, registration, and refresh requests never trigger automatic refresh. Token rotation updates both stored tokens, and logout or a newer login prevents an older in-flight refresh from restoring the previous session.
Network errors, timeouts, rate limits, and server failures preserve stored credentials
and the loaded user while surfacing the error. Rejected refresh tokens or an unauthorized
retry invalidate the session and clear both tokens. A 403 from /me also clears the
session (the API uses it for inactive users); permission errors on other endpoints do
not. A wrong current password can return 401 without invalidating the session.
.
├── apps/
│ ├── api/ # FastAPI service (Python/uv)
│ ├── web/ # Next.js app (TypeScript/pnpm)
│ └── rust-svc/ # Rust binary service
├── packages/
│ ├── py-core/ # PyO3 crate → native .so for apps/api
│ ├── wasm-core/ # wasm-bindgen crate → WASM for apps/web
│ └── ts-shared/ # Shared TypeScript types
├── nginx/
├── prometheus/
├── grafana/
├── Cargo.toml # Cargo workspace root
├── pyproject.toml # uv workspace root
├── pnpm-workspace.yaml
├── docker-compose.yml
└── Makefile
Install these once on your machine before anything else:
| Tool | Version | Install |
|---|---|---|
| Rust + cargo | stable | curl https://sh.rustup.rs -sSf | sh |
| Python | ≥ 3.12 | python.org or brew install python |
| uv | latest | curl -LsSf https://astral.sh/uv/install.sh | sh |
| Node.js | ≥ 22 | brew install node or nodejs.org |
| pnpm | 9.14.4 (pinned in root package.json) |
corepack enable or npm install -g pnpm@9.14.4 |
| Moon | latest | curl -fsSL https://moonrepo.dev/install/moon.sh | bash |
| Docker Desktop | latest | docker.com — needed for the full-stack path |
Optional (only needed when you start using the Rust extension stubs):
pip install maturin # PyO3 → Python .so (packages/py-core)
cargo install wasm-pack # Rust → WASM (packages/wasm-core)Verify your environment:
cargo --version && python3 --version && uv --version
node --version && pnpm --version && moon --versionReplace placeholders across the repo:
# macOS / Linux
grep -rl '{{PROJECT_NAME}}' . | xargs sed -i '' 's/{{PROJECT_NAME}}/My App/g'
grep -rl '{{PROJECT_NAME_SLUG}}' . | xargs sed -i '' 's/{{PROJECT_NAME_SLUG}}/my-app/g'
grep -rl 'project-name-slug' . | xargs sed -i '' 's/project-name-slug/my-app/g'cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.envMinimum values to set in apps/api/.env:
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/myapp
JWT_SECRET_KEY=<generate below>
REDIS_URL=redis://localhost:6379/0Generate a JWT secret:
python3 -c "import secrets; print(secrets.token_urlsafe(32))"Set in apps/web/.env (or .env.local for local-only override):
NEXT_PUBLIC_API_URL=http://localhost:8000make install
# Runs: uv sync (Python) + pnpm install (Node) + cargo fetch (Rust)Run pnpm from the repository root to install both apps/web and
packages/ts-shared. pnpm-lock.yaml is the only JavaScript lockfile; use
pnpm install --frozen-lockfile for reproducible installs.
Verify the build graph works:
moon run :build
# rust-svc compiles; api wheel builds; py-core/wasm-core/web skip gracefully
# until maturin/wasm-pack/pnpm-install are completeYou still need Postgres and Redis running. The quickest way is Docker for just those two:
docker compose up -d postgres redisThen run migrations and start the servers:
# 1. Apply database migrations
cd apps/api && uv run alembic upgrade head && cd ../..
# 2. (Optional) Load fixture data
make seed # creates admin@example.com / ChangeMe123!
# 3. Start API (port 8000) + web (port 3000) in parallel
make devOpen:
- Web app → http://localhost:3000
- API docs → http://localhost:8000/api/docs
- API health → http://localhost:8000/api/v1/health
Builds and starts the default services (Postgres, Redis, API, Celery, Web, nginx, Prometheus, Grafana, pgAdmin):
docker compose up -d --buildrust-svc is an unfinished binary scaffold: it prints a startup message and exits,
so it is excluded from default startup by the opt-in rust profile in both
docker-compose.yml and docker-compose.prod.yml. Before enabling it, implement
a persistent HTTP server listening on 0.0.0.0:8080 with a /health endpoint.
Then include it with docker compose --profile rust up -d --build (or
docker compose -f docker-compose.prod.yml --profile rust up -d --build for
production). Explicitly targeting rust-svc also enables it regardless of profiles.
Wait ~30 s for migrations and health checks, then:
# (Optional) Seed fixture data inside the running container
make seedOpen:
- Web app → https://localhost (nginx, self-signed cert — accept the browser warning)
- API docs → http://localhost:8000/api/docs
- Prometheus → http://localhost:9091
- Grafana → http://localhost:3001 (admin / admin)
- pgAdmin → http://localhost:5050
Runs API and web with source mounted for live reload, nginx for TLS:
# First time only — installs deps into named Docker volumes
make docker-dev-prepare
# Start api + web + nginx with hot reload
make docker-dev-upMoon manages build ordering across languages automatically:
moon run :build # builds everything in dependency order (cached)
moon run :test # tests everything
moon run web:build # builds web after wasm-core + ts-shared
moon run api:build # builds api after py-coreBuild graph:
api:build ← py-core:build (Rust → Python .so via maturin)
web:build ← wasm-core:build (Rust → WASM via wasm-pack)
web:build ← ts-shared:build (TypeScript types)
- Lint/format:
make lintandmake format(runs ruff/black/isort for Python, eslint/prettier for TypeScript). - Type checks:
make api-typecheck(mypy),make web-typecheck(Next.js route types + TypeScript for the pnpm workspace). - Frontend build:
make web-build(shared TypeScript package, then Next.js). - Git hooks:
make pre-commit-install, then run on demand withmake pre-commit. - Sample data:
make seedloads fixture users (admin@example.com/ChangeMe123!) and basic audit events.
Canonical examples: apps/api/.env.example and apps/web/.env.example
| Variable | Where | Notes |
|---|---|---|
DATABASE_URL |
api | Postgres connection string |
JWT_SECRET_KEY |
api | Generate with secrets.token_urlsafe(32) |
REDIS_URL |
api | For Celery + rate limiting |
NEXT_PUBLIC_API_URL |
web | http://localhost:8000 (local), https://localhost (Docker) |
LLM_PROVIDER / LLM_MODEL / LLM_API_KEY |
api | Optional — for the agent scaffold |
- Backend workers: Single Uvicorn process by default. For horizontal scale, run multiple replicas behind nginx and set
UVICORN_WORKERS. Keep Alembic migrations as a one-shot job. - Celery: Scale
celery-workerreplicas independently.celery-beatmust stay singleton. - Sessions/state: Stateless JWT auth — safe to scale horizontally. Redis handles rate limiting.
- Redis persistence: Disabled by default. Enable AOF/RDB or use a managed Redis for durable state.
make api-test # pytest
make web-test # Playwright e2e
make rust-test # Cargo workspace tests
moon run :test # all projects (cached)Install the test browser once with
pnpm --filter frontend exec playwright install chromium. make web-test
builds the frontend first; Playwright then starts the production server
automatically (or reuses an existing local server on port 3000). When invoking
pnpm --filter frontend test:e2e directly, run make web-build first.
Set PLAYWRIGHT_BASE_URL to test an already-running deployment instead.
Run make api-install before the Playwright suite: cross-origin authentication
tests launch an isolated API fixture from the workspace .venv, without requiring
Postgres or Redis. Set PLAYWRIGHT_API_PYTHON to use another API-ready interpreter.
.github/workflows/ci.yml installs the pnpm workspace with the root lockfile
and runs production dependency audits, API lint/tests, frontend lint,
workspace type checks, shared-package and Next.js builds, and Chromium
Playwright tests against the production build. Playwright starts and stops
the Next.js production server and uploads its report and traces for seven
days. The Rust job checks formatting, runs Clippy with warnings denied, and
tests every Cargo workspace member using Cargo.lock.
- Add your domains: extend
apps/api/src/domainandapps/api/src/api/v1 - Expand
apps/web/srcpages/components for your use case - Add Rust logic to
packages/py-core(PyO3) orpackages/wasm-core(WASM) and import it - Extend the CI checks as you add services and configure deployment for your environment