# CLAUDE.md — Coffee Project Backend See also the monorepo-level `../CLAUDE.md` for full architecture overview. ## Commands ```bash uv sync # Install dependencies uv run uvicorn cpv3.main:app --reload # Dev server (localhost:8000) uv run pytest # Run all tests uv run pytest tests/integration/.py # Single test file uv run alembic revision --autogenerate -m "msg" # Create migration uv run alembic upgrade head # Apply migrations uv run dramatiq cpv3.modules.tasks.service # Start background worker uv run ruff check cpv3/ # Lint uv run ruff format cpv3/ # Auto-format docker-compose up # Full stack (DB, Redis, MinIO, API, Worker) ``` ## Architecture Layered module pattern. Each module has exactly 5-6 files (`__init__.py`, `models.py`, `schemas.py`, `repository.py`, `service.py`, `router.py`). No subdirectories within modules. When in doubt, put logic in `service.py`. ``` cpv3/ ├── main.py # FastAPI app, CORS, router registration ├── api/v1/router.py # Aggregates all module routers ├── common/schemas.py # Base Schema class (Pydantic, from_attributes=True) ├── db/ # Base, session, model imports ├── infrastructure/ # Auth, security, settings, storage └── modules/ # Feature modules (users, projects, media, files, etc.) ``` ## Key Patterns - **Database**: PostgreSQL 16, async SQLAlchemy (`AsyncSession`), all models inherit `Base` + `BaseModelMixin`. - **Soft deletes**: `is_deleted` flag — repositories filter by default. - **Auth**: JWT access + refresh tokens in HTTP-only cookies. Use `get_current_user` dependency. - **Config**: `get_settings()` from `cpv3/infrastructure/settings.py` (cached with `@lru_cache`). - **Background tasks**: Dramatiq with Redis broker. Actors live in `cpv3/modules/tasks/service.py`. - **Package manager**: **uv** only — `uv sync`, `uv add `, `uv run `. - **Linting**: Ruff (line length 100, config in `pyproject.toml`). ## Common Gotchas - **Async sessions**: Never mix sync and async DB operations. Use `await` on all session calls. - **Module structure**: Do not create extra files or subdirectories. Standard files only: `models.py`, `schemas.py`, `repository.py`, `service.py`, `router.py`. - **Schemas**: Always inherit from `cpv3.common.schemas.Schema`, not raw Pydantic `BaseModel`. - **Imports**: Use absolute paths (`from cpv3.modules.media.schemas import ...`), not relative. - **Error messages**: Store as module-level constants with `ERROR_` prefix, not inline strings. - **CPU-bound work**: Use `anyio.to_thread.run_sync()` to avoid blocking the event loop. ## Docker Services ``` postgres → localhost:5332 minio → localhost:9000 (console: 9001) redis → localhost:6379 api → localhost:8000 (OpenAPI at /api/schema/) ```