Skip to content

ADR-005: Backend Framework

Context and Problem Statement

The portfolio backend needs to handle a small number of API endpoints: a health check, a contact form submission (with email sending), and a CV file download. A backend framework was needed that could be packaged as a small container, handle async I/O for email and file operations, and provide automatic request validation.

Considered Options

  • FastAPI — a modern async Python web framework. Uses Pydantic for request/response validation and automatically generates an OpenAPI schema. Minimal boilerplate for a small API surface. Built on Starlette and Uvicorn.

  • Django REST Framework — a mature, batteries-included framework. Brings an ORM, admin panel, and many features that are unnecessary for an API with three endpoints and no database.

  • Flask — a lightweight Python micro-framework. No built-in validation, no automatic OpenAPI generation, and synchronous by default (async support added later). Would require additional libraries to match FastAPI's out-of-the-box feature set.

  • Node.js / Express — JavaScript on the server. Avoids a second language in the stack (frontend is TypeScript), but Python is the preferred language for backend work in this context, and FastAPI's Pydantic models provide stronger validation guarantees than typical Express middleware.

Decision Outcome

Chosen option: "FastAPI". The API surface is small and stable. FastAPI's Pydantic-based validation eliminates manual input checking, and the auto-generated OpenAPI schema (available at /docs and /openapi.json at runtime) serves as living documentation. The async model handles email SMTP calls and file I/O without blocking. The framework adds minimal overhead to the container image.

Consequences

  • Good, because Pydantic models enforce request validation at the framework level — invalid payloads are rejected with structured error responses before reaching application logic.
  • Good, because FastAPI auto-generates an OpenAPI schema from route definitions and Pydantic models, keeping API documentation in sync with the code.
  • Bad, because Python adds a runtime dependency to the backend container. If the backend were to grow significantly in throughput-sensitive operations, Python's GIL could become a constraint (though async mitigates much of this for I/O-bound work).
  • Bad, because FastAPI is less opinionated than Django — as the API grows, conventions for project structure, database access, and authentication must be established manually.

Page history

Field Value
Last updated