# Monorepo Scaffold — Implementation Document > **Issue:** [#1 — Set up monorepo scaffold with Yarn workspaces for Web and API](https://gitea-dev.codescripters.org/crisleo-hermes/job-tracker/issues/1) > > **Branch:** `feature/setup-monorepo-scaffold` > > **Status:** ✅ Implemented ## Project Structure ``` job-tracker/ ├── package.json # Yarn workspaces root ("web", "api") ├── docker-compose.yml # PostgreSQL 15 + Django API + React/Vite web ├── README.md ├── .gitignore │ ├── api/ # Django REST API (Python 3.12) │ ├── Dockerfile │ ├── pyproject.toml # uv-managed Python dependencies │ ├── uv.lock │ ├── manage.py # Django management CLI entrypoint │ └── project/ │ ├── __init__.py │ ├── settings.py # PostgreSQL config via DATABASE_URL │ ├── urls.py # Root URL configuration │ └── wsgi.py # WSGI application │ └── web/ # React 19 + Vite frontend ├── Dockerfile ├── package.json # React 19, Vite 6, @vitejs/plugin-react ├── vite.config.js # Dev server on 0.0.0.0:3000, polling ├── index.html # HTML entry point └── src/ ├── main.jsx # ReactDOM.createRoot mount └── App.jsx # Basic App component ``` --- ## 1. Root `package.json` — Yarn Workspaces **File:** `package.json` ```json { "name": "job-tracker", "private": true, "workspaces": ["web", "api"] } ``` - Declares `web` and `api` as Yarn workspace members. - Dependencies from each workspace are hoisted to the root `node_modules/` where possible. > **Note:** `"api"` is listed as a workspace member purely for the monorepo structure. The actual Python/Django dependencies are managed via `uv` (see Section 3). --- ## 2. Docker Compose — Development Environment **File:** `docker-compose.yml` Three services orchestrated for local development: ### `db` (PostgreSQL 15) | Setting | Value | |------------------|------------------------------------------| | Image | `postgres:15` | | DB Name | `jobtracker` | | User / Password | `jobtracker` / `jobtracker` | | Port | `5432` | | Volume | `postgres_data:/var/lib/postgresql/data` | | Health Check | `pg_isready -U jobtracker -d jobtracker` (5s interval, 10 retries) | ### `api` (Django) | Setting | Value | |------------------|-----------------------------------------------| | Build Context | `./api` | | Port | `8000` | | Env Vars | `DATABASE_URL=postgres://jobtracker:...@db:5432/jobtracker` | | Volumes | `./api:/app` (live code reload) | | Command | `python manage.py migrate && python manage.py runserver 0.0.0.0:8000` | | Depends On | `db` → `condition: service_healthy` | ### `web` (React + Vite) | Setting | Value | |------------------|-----------------------------------------------| | Build Context | `./web` | | Port | `3000` | | Env Vars | `REACT_APP_API_URL=http://localhost:8000` | | Volumes | `./web:/app` + `/app/node_modules` (live reload) | | Depends On | `db` → `condition: service_healthy` | ### How to start ```bash docker compose up ``` - **Web:** http://localhost:3000 - **API:** http://localhost:8000 - **DB:** `postgres://jobtracker:jobtracker@localhost:5432/jobtracker` --- ## 3. Django API (`/api`) ### Dependency Management with `uv` **File:** `api/pyproject.toml` ```toml [project] name = "api" version = "1.0.0" requires-python = ">=3.12" dependencies = [ "django>=5.1,<6.0", "psycopg2-binary>=2.9", ] ``` Install and verify: ```bash cd api uv sync uv run python manage.py check # → System check identified no issues (0 silenced). ``` ### Database Configuration (`settings.py`) The `DATABASES` setting is built dynamically from the `DATABASE_URL` environment variable: ```python DATABASE_URL = "postgres://user:password@host:port/dbname" ``` Parsed into the standard Django `DATABASES` dict with: - `ENGINE`: `django.db.backends.postgresql` - `NAME`, `USER`, `PASSWORD`, `HOST`, `PORT` — all extracted from the URL Default value when `DATABASE_URL` is unset: `postgres://jobtracker:jobtracker@localhost:5432/jobtracker` ### Installed Apps (Minimal) ```python INSTALLED_APPS = [ "django.contrib.contenttypes", "django.contrib.auth", ] ``` ### Files Created | File | Purpose | |----------------------|--------------------------------------------| | `api/Dockerfile` | Python 3.12-slim + uv + libpq → Django dev | | `api/manage.py` | Django CLI entrypoint | | `api/pyproject.toml` | uv project config with Django + psycopg2 | | `api/project/__init__.py` | Python package marker | | `api/project/settings.py` | Django settings with PostgreSQL | | `api/project/urls.py` | Root URL configuration | | `api/project/wsgi.py` | WSGI application | --- ## 4. React Frontend (`/web`) ### Dependencies | Package | Version | Type | |----------------------|---------|----------------| | `react` | ^19.0.0 | dependency | | `react-dom` | ^19.0.0 | dependency | | `vite` | ^6.0.0 | devDependency | | `@vitejs/plugin-react` | ^4.3.0 | devDependency | ### Vite Configuration **File:** `web/vite.config.js` ```js export default defineConfig({ plugins: [react()], server: { host: "0.0.0.0", port: 3000, watch: { usePolling: true }, // required for Docker volume mounts }, }); ``` ### Entry Point **File:** `web/src/main.jsx` — mounts `` inside `React.StrictMode` on the `#root` element. ### Component **File:** `web/src/App.jsx` — minimal functional component: ```jsx function App() { return (

Job Tracker

Welcome to the Job Tracker application.

); } ``` ### Files Created | File | Purpose | |-----------------------|-----------------------------------------------| | `web/Dockerfile` | Node 20-alpine + yarn → dev server on :3000 | | `web/package.json` | React 19 + Vite 6 dependencies | | `web/vite.config.js` | Vite dev server config with polling | | `web/index.html` | HTML shell loading `/src/main.jsx` | | `web/src/main.jsx` | React entry point | | `web/src/App.jsx` | Basic App component | --- ## 5. Dockerfiles ### `api/Dockerfile` ```dockerfile FROM python:3.12-slim WORKDIR /app RUN apt-get update && apt-get install -y libpq-dev gcc && rm -rf /var/lib/apt/lists/* COPY pyproject.toml ./ RUN pip install uv && uv sync --frozen COPY . . EXPOSE 8000 CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] ``` ### `web/Dockerfile` ```dockerfile FROM node:20-alpine WORKDIR /app COPY package.json ./ RUN yarn install --frozen-lockfile COPY . . EXPOSE 3000 CMD ["yarn", "dev"] ``` --- ## Verification ### Django (standalone) ```bash cd api uv sync uv run python manage.py check # → System check identified no issues (0 silenced). ``` ### Frontend (standalone) ```bash cd web yarn install yarn dev # → Vite dev server running on http://localhost:3000 ``` ### Full stack (Docker) ```bash docker compose up --build ``` --- ## Environment Variables Summary | Variable | Default | Used By | |-------------------|------------------------------------------------------|---------| | `DATABASE_URL` | `postgres://jobtracker:jobtracker@localhost:5432/jobtracker` | API | | `REACT_APP_API_URL` | `http://localhost:8000` | Web | | `DJANGO_SECRET_KEY` | `django-insecure-change-me-in-production` | API | | `DJANGO_DEBUG` | `True` | API |