3.0 KiB
3.0 KiB
CLAUDE.md — Coffee Project Backend
See also the monorepo-level ../CLAUDE.md for full architecture overview.
Commands
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/<file>.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 inheritBase+BaseModelMixin. - Soft deletes:
is_deletedflag — repositories filter by default. - Auth: JWT access + refresh tokens in HTTP-only cookies. Use
get_current_userdependency. - Config:
get_settings()fromcpv3/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 <pkg>,uv run <cmd>. - Linting: Ruff (line length 100, config in
pyproject.toml).
Common Gotchas
- Async sessions: Never mix sync and async DB operations. Use
awaiton 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 PydanticBaseModel. - 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/)