Skip to content

Backend Development

Guide to developing the Patrimoine backend — a FastAPI application running on Python 3.13 with SQLAlchemy 2.0 async, Alembic migrations, and SQLite (or PostgreSQL).


  • Python 3.13
  • uv — the Python package manager

Terminal window
cd backend
uv sync --extra dev

The --extra dev flag installs pytest and ruff in addition to runtime dependencies.


Terminal window
uv run ruff check .

Auto-fix:

Terminal window
uv run ruff check --fix .

Ruff configuration lives in backend/pyproject.toml. See Code Style & Linting for rule details.


Terminal window
uv run pytest tests/ -q

Tests use asyncio_mode=auto (configured in pyproject.toml). The suite contains about 6,300 tests across 310 files (uv run pytest tests --collect-only -q | tail -1 gives the current number). A filterwarnings entry suppresses the known aiosqlite _connection_worker_thread event-loop-closed warning that occurs when the background thread outlives the test event loop. See Testing for fixture details.


Terminal window
uv run uvicorn app.main:app --reload --port 8000

The API is available at http://localhost:8000. Interactive docs at http://localhost:8000/docs, served when DEBUG=true or EXPOSE_API_DOCS=true is set (both are off by default).


  1. Generate a new revision:

    Terminal window
    uv run alembic revision -m "description"
  2. Open the new file in alembic/versions/ and write the upgrade() and downgrade() functions.

  3. Use a sequential numeric revision ID — the next number after the latest migration (for example, with 129 as the latest, the next is 130).

  4. Test the migration round-trip:

    Terminal window
    uv run alembic upgrade head
    uv run alembic downgrade base

  1. Create app/routers/thing.py:

    from fastapi import APIRouter
    router = APIRouter()
  2. Define endpoints using Depends(get_current_user) for auth and Depends(get_db) for database access.

  3. Register the router in app/main.py:

    app.include_router(thing.router, prefix="/api/v1/things", tags=["things"])
  4. Add a test in backend/tests/ (happy path plus one failure case).

  5. Regenerate the OpenAPI schema:

    Terminal window
    cd backend && uv run python scripts/export_openapi.py
  6. Bump version in backend/pyproject.toml (feat = minor, fix = patch), run cd backend && uv lock and re-export openapi.json; see Contributing.


  1. Create app/models/thing.py:

    from app.models.base import Base, TimestampMixin
    class Thing(Base, TimestampMixin):
    ...
  2. Export the model in app/models/__init__.py.

  3. Create a migration (see Creating a Migration).

  4. Create Pydantic schemas in app/schemas/thing.py (ThingCreate, ThingUpdate, ThingOut).

  5. Create a service in app/services/thing_service.py.


After any API change (new endpoint, modified schema, etc.), regenerate and commit openapi.json:

Terminal window
cd backend && uv run python scripts/export_openapi.py

CI verifies the schema is up to date with the --check flag:

Terminal window
cd backend && uv run python scripts/export_openapi.py --check

If CI fails on this step, run the export command locally and commit the updated openapi.json.