Skip to content
← How we build

adr

ADR-001: Use FastAPI for the API Layer

Status

Accepted (reflects existing implementation — backend/src/api/server.py)

Context

MarketCompass needs an API layer that: supports async I/O (LLM calls, external financial APIs are all network-bound), supports streaming responses (SSE for token-by-token chat output, per backend/src/streaming/), generates typed request/response schemas for a portfolio-quality developer experience (Swagger/OpenAPI), and integrates cleanly with pydantic/pydantic-settings, which the project already uses for configuration (config/settings.py).

Decision

Use FastAPI as the API framework, with route modules per resource (chat.py, agents.py, health.py, providers.py) and a single app factory (server.py).

Alternatives Considered

Option Rejected because
Flask No native async support without extensions; no built-in request/response schema validation; would require bolting on Pydantic manually
Django REST Framework Heavyweight for an API-only service with no ORM-centric data model in Phase 1; slower iteration for a portfolio timeline
Raw ASGI (Starlette only) FastAPI is Starlette plus schema validation and docs generation — using raw Starlette would mean re-implementing what FastAPI already provides for free

Consequences

Positive:

  • Automatic OpenAPI docs at /docs (already working per README_BACKEND.md) — valuable for interview demos.
  • Native async def route handlers match the I/O-bound nature of every downstream Tool call.
  • Pydantic models double as both request/response validation and the internal RoutingDecision/SkillResult/ToolResult contracts described in the architecture docs, keeping one schema philosophy end-to-end.

Negative / accepted tradeoffs:

  • FastAPI's dependency-injection system is not used for Skill/Tool registry resolution in Phase 1 (the Orchestrator resolves registries directly) to avoid coupling core orchestration logic to a web-framework-specific DI mechanism — this preserves the "avoid framework lock-in" principle from project-context.md. FastAPI's DI is used only for request-scoped API concerns (e.g. auth, request ID).

Interview Talking Point

FastAPI was chosen for I/O concurrency and schema-first design, but orchestration logic (Router/Skills/Tools) deliberately does not depend on FastAPI at all — it could be mounted behind Flask, a CLI, or a message queue consumer without modification. That separation is itself a design decision, not an accident.

Source: docs/adr/ADR-001-fastapi.md

Follow the work.

Occasional updates on SignalFoundry, MarketCompass, and what we are building at CompassFoundry Labs.

No spam. Unsubscribe anytime.