Skip to content

ADR-003: Use FastAPI for Backend

Status: Accepted
Date: 2024-11-25
Deciders: Development Team

Context

We need a Python web framework for RESTful APIs with async I/O, automated documentation, type validation, and fast development. The main contenders were FastAPI and Django + Django REST Framework.

Decision

Use FastAPI as the backend framework.

Why FastAPI

  • Async-native (ASGI/Starlette) for better concurrency
  • Automatic OpenAPI docs at /docs (EPFL requirement ✅)
  • Type safety via Pydantic reduces runtime errors
  • Minimal boilerplate compared to Django
  • Database agnostic (works with any async ORM)
  • One of the fastest Python frameworks available

Trade-offs

FastAPI wins on:

  • Performance (async I/O)
  • Developer experience (less code)
  • API documentation (auto-generated)
  • Type safety (Pydantic validation)

Django wins on:

  • Maturity (2005 vs 2018)
  • Built-in admin UI
  • Batteries-included ecosystem

We chose performance and modern async patterns over Django's admin UI, which we can build separately if needed.

Implementation

Stack:

  • FastAPI + Uvicorn (ASGI server)
  • SQLAlchemy 2.x (async ORM) + Alembic (migrations)
  • Pydantic 2.x (validation) + pydantic-settings
  • authlib (OAuth2/OIDC) + joserfc (JWT)

See backend/pyproject.toml for current versions.

Example endpoint:

from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession

router = APIRouter(prefix="/emissions", tags=["emissions"])

@router.post("/", response_model=EmissionResponse)
async def create_emission(
    data: EmissionCreate,
    db: AsyncSession = Depends(get_db),
    user: User = Depends(get_current_user)
):
    """Calculate and store CO2 emission."""
    emission = await emission_service.calculate(db, data, user)
    return emission

References