Skip to content

Fast Project Structure

Scaling a FastAPI project means moving from a single main.py file to a well-organized, modular folder structure.


Here’s a recommended structure used in production-grade FastAPI apps:

project/
│── app/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ main.py # FastAPI app entrypoint
β”‚ β”œβ”€β”€ core/ # Core configuration
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ config.py # Settings (env vars, DB URL, etc.)
β”‚ β”‚ β”œβ”€β”€ security.py # JWT / Auth utils
β”‚ β”‚ └── logging.py # Logging config
β”‚ β”‚
β”‚ β”œβ”€β”€ api/ # API routes
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ deps.py # Shared dependencies (Depends)
β”‚ β”‚ β”œβ”€β”€ v1/ # Versioned APIs
β”‚ β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”‚ β”œβ”€β”€ routes_users.py
β”‚ β”‚ β”‚ β”œβ”€β”€ routes_items.py
β”‚ β”‚ β”‚ └── routes_auth.py
β”‚ β”‚ └── v2/ # Future APIs
β”‚ β”‚
β”‚ β”œβ”€β”€ models/ # SQLAlchemy / Pydantic models
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ user.py
β”‚ β”‚ └── item.py
β”‚ β”‚
β”‚ β”œβ”€β”€ schemas/ # Pydantic schemas
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ user.py
β”‚ β”‚ └── item.py
β”‚ β”‚
β”‚ β”œβ”€β”€ services/ # Business logic
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ user_service.py
β”‚ β”‚ └── item_service.py
β”‚ β”‚
β”‚ β”œβ”€β”€ db/ # Database session & migrations
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ base.py # Base metadata
β”‚ β”‚ β”œβ”€β”€ session.py # SQLAlchemy SessionLocal
β”‚ β”‚ └── init_db.py
β”‚ β”‚
β”‚ β”œβ”€β”€ tests/ # Unit & integration tests
β”‚ β”‚ β”œβ”€β”€ __init__.py
β”‚ β”‚ β”œβ”€β”€ test_auth.py
β”‚ β”‚ β”œβ”€β”€ test_users.py
β”‚ β”‚ └── conftest.py
β”‚ β”‚
β”‚ └── utils/ # Helper functions
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ email.py
β”‚ └── hashing.py
β”‚
│── alembic/ # Migrations (if using Alembic)
│── .env # Environment variables
│── requirements.txt
│── pyproject.toml
│── Dockerfile
│── docker-compose.yml

  • config.py β†’ Pydantic BaseSettings for environment variables.
  • security.py β†’ JWT utils, password hashing.
  • logging.py β†’ Centralized logging configuration.

πŸ‘‰ Keeps global setup separate.


  • Organized by version (v1, v2).
  • Each module (e.g., users, items) has its own router file.
  • deps.py β†’ common dependencies (get_db, get_current_user).

πŸ‘‰ Encourages modular APIs.


  • models/ β†’ SQLAlchemy ORM models (DB representation).
  • schemas/ β†’ Pydantic models (input/output validation).

πŸ‘‰ Keeps DB models separate from request/response models.


  • Encapsulates actual logic (e.g., creating users, applying discounts).
  • Keeps routers slim β†’ endpoints only orchestrate calls.

  • session.py β†’ SQLAlchemy SessionLocal.
  • init_db.py β†’ Startup logic (e.g., creating default admin).

  • Organized by feature.
  • Uses pytest, httpx.AsyncClient.
  • conftest.py for fixtures (e.g., test DB session).

  • Utility functions (e.g., hashing, sending emails, formatting).

from pydantic import BaseSettings
class Settings(BaseSettings):
PROJECT_NAME: str = "My FastAPI App"
DATABASE_URL: str
SECRET_KEY: str
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
class Config:
env_file = ".env"
settings = Settings()

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.core.config import settings
engine = create_engine(settings.DATABASE_URL, future=True, echo=True)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from app.db.session import SessionLocal
from app.schemas.user import UserCreate, UserOut
from app.services.user_service import create_user
router = APIRouter()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@router.post("/users", response_model=UserOut)
def register_user(user: UserCreate, db: Session = Depends(get_db)):
return create_user(db, user)

from sqlalchemy.orm import Session
from app.models.user import User
from app.schemas.user import UserCreate
from app.utils.hashing import hash_password
def create_user(db: Session, user: UserCreate):
db_user = User(username=user.username, hashed_password=hash_password(user.password))
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user

βœ… Use versioned APIs (/api/v1, /api/v2).

βœ… Keep models, schemas, services, and routes separate.

βœ… Use Dependency Injection (Depends) for DB sessions, security.

βœ… Write unit & integration tests for each module.

βœ… Use Alembic for migrations.

βœ… Use .env for secrets, never hardcode credentials.

βœ… Use Docker & docker-compose for reproducible environments.

βœ… Add logging & monitoring early.