This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Install dependencies (creates .venv automatically)
uv sync
# Install with dev dependencies
uv sync --dev
# Run the API server
uv run uvicorn main:app --reload --port 8000
# Run all tests
uv run pytest tests/
# Run a single test
uv run pytest tests/test.py::test_create_user_success -v
# Add a new dependency
uv add <package>
# Start Kafka + Zookeeper (required for Kafka features)
docker-compose up -dThis is a FastAPI backend following hexagonal (ports-and-adapters) architecture with Domain-Driven Design principles.
| Layer | Path | Role |
|---|---|---|
| HTTP | app/api/routers/ |
Route definitions, Depends() wiring only |
| Use Cases | app/infrastructure/services/ |
Business operations (CreateUser, LoginUser) |
| Interfaces (Ports) | app/interfaces/ |
Abstract base classes defining contracts |
| Adapters | app/database/crud/, app/infrastructure/security/ |
Concrete implementations of interfaces |
| ORM Models | app/database/models/ |
SQLAlchemy table definitions |
| Pydantic Schemas | app/database/schemas/, app/api/schemas/ |
Request/response validation and DTOs |
| Config | app/core/config.py |
Settings via pydantic-settings (reads .env) |
FastAPI Depends() chains are assembled in app/utils/dependencies.py. A request for CreateUser follows: get_async_db → UserSQLAlchemyRepository + BcryptPasswordHasher → CreateUser. This is the pattern to follow when adding new use cases.
- Dev/test uses SQLite (
sqlite+aiosqlite:///./test.db) hardcoded inapp/database/session.py. Production switches to PostgreSQL viaDATABASE_URLenv var inapp/core/config.py(the two are not yet wired together). - SQLAlchemy 2.0 async engine; sessions are request-scoped via
get_async_db()generator. - Tables auto-created on startup via
lifespaninmain.pycallinginit_db().
JWT tokens signed with HS256. create_access_token / decode_access_token live in app/utils/jwt_handler.py. The get_current_user dependency in dependencies.py validates the bearer token and returns the ORM user. jose must be installed but is not currently in requirements.txt.
app/communication/communication_handler.py provides a lightweight in-process pub/sub (subscribe / post_event). This is separate from the Kafka integration in app/utils/producer.py + app/utils/consumer.py, which is used for external async messaging (requires a running broker via docker-compose).
UserRole enum in app/database/schemas/user.py: admin, client, candidate, staff. Default role is candidate.
Copy .env and set:
DATABASE_URL=postgresql+asyncpg://user:pass@localhost:5432/patron_dev
SECRET_KEY=<hex string>
ACCESS_TOKEN_EXPIRE_MINUTES=30
Without .env, the app falls back to SQLite in-memory and a hardcoded SECRET_KEY from config.py.