Skip to main content

Project Structure

FastAPI AI Kit follows a clean, domain-driven layout. Every module is decoupled — delete what you don't need without breaking the rest.

app/
├── api/                    # FastAPI routers
│   ├── auth.py             # Register, login, token refresh
│   ├── api_keys.py         # Issue, list, revoke API keys
│   ├── chat.py             # /v1/chat — LLM endpoint (streaming + buffered)
│   ├── rag.py              # /v1/rag — ingest + query endpoints
│   └── health.py           # /healthz
│
├── core/                   # Cross-cutting concerns
│   ├── config.py           # Settings via Pydantic BaseSettings
│   ├── database.py         # Async SQLAlchemy engine + session factory
│   ├── redis.py            # Redis connection pool
│   ├── security.py         # JWT encode/decode, password hashing
│   └── middleware.py       # Rate limiting, request logging
│
├── models/                 # SQLAlchemy ORM models
│   ├── user.py
│   ├── api_key.py
│   └── usage.py            # Token usage records per key
│
├── schemas/                # Pydantic request/response models
│   ├── auth.py
│   ├── chat.py
│   └── rag.py
│
├── services/               # Business logic layer
│   ├── auth_service.py     # User registration, token issuance
│   ├── llm_service.py      # Unified LLM abstraction
│   ├── rag_service.py      # Document ingestion + retrieval
│   ├── billing_service.py  # Usage metering, Stripe hooks
│   └── key_service.py      # API key lifecycle
│
├── repositories/           # Data access layer (DB queries)
│   ├── user_repo.py
│   ├── key_repo.py
│   └── usage_repo.py
│
├── workers/                # Background tasks
│   ├── celery_app.py
│   ├── tasks.py            # Async jobs (embedding, email, etc.)
│   └── arq_app.py          # arq alternative (lighter weight)
│
├── migrations/             # Alembic migration scripts
└── tests/                  # pytest test suite
    ├── conftest.py
    ├── test_auth.py
    ├── test_chat.py
    └── test_rag.py

Key design decisions

Services over fat routes

Business logic lives in services/, not in route handlers. Routes validate input, call a service, and return the result. This keeps routes thin and testable.

Repositories for DB access

Database queries are isolated in repositories/. Services call repositories, never raw SQLAlchemy sessions directly. This makes testing straightforward — swap the real repo for a mock.

Config via Pydantic BaseSettings

All configuration flows through app/core/config.py. Environment variables are validated on startup. Accessing settings.OPENAI_API_KEY is always type-safe and never None if the app booted.

Async everywhere

The codebase is async from top to bottom — FastAPI, SQLAlchemy async, aioredis. No blocking calls on the event loop. This matters at scale.