How to Structure Your FastAPI Projects (The Right Way)

Master the art of structuring FastAPI apps like a pro — whether you’re building APIs solo or scaling with a team.

How to Structure Your FastAPI Projects (The Right Way)
Photo by Med Badr Chemmaoui on Unsplash

Most FastAPI apps start as a single file… until they don’t.

How to Structure Your FastAPI Projects (The Right Way)

FastAPI has taken the Python web community by storm — and for good reason. It’s fast, intuitive, and perfect for building modern APIs. But as your application grows beyond a few endpoints, structuring your FastAPI project properly becomes absolutely critical.

A well-structured project is easier to maintain, scale, test, and onboard new developers into. In this article, we’ll break down the ideal structure for a FastAPI project and explain why each part exists.
Whether you’re starting a new project or looking to refactor an existing one, this guide has you covered.

The Problem with a “Single File” FastAPI App

Let’s be honest — we’ve all done this:

# main.py 
 
from fastapi import FastAPI 
 
app = FastAPI() 
 
@app.get("/") 
def read_root(): 
    return {"message": "Hello, world"}
Simple. Clean. Great for tutorials.

But once you add authentication, database connections, business logic, background tasks, and multiple routers… this file becomes a monolith. It’s no longer maintainable.

The Modular Structure That Just Works

Here’s a recommended folder structure for real-world FastAPI projects:

fastapi_project/ 
│ 
├── app/ 
│   ├── api/ 
│   │   ├── __init__.py 
│   │   ├── deps.py 
│   │   ├── v1/ 
│   │   │   ├── __init__.py 
│   │   │   ├── endpoints/ 
│   │   │   │   ├── __init__.py 
│   │   │   │   ├── users.py 
│   │   │   │   └── auth.py 
│   ├── core/ 
│   │   ├── __init__.py 
│   │   ├── config.py 
│   │   ├── security.py 
│   ├── models/ 
│   │   ├── __init__.py 
│   │   ├── user.py 
│   ├── schemas/ 
│   │   ├── __init__.py 
│   │   ├── user.py 
│   ├── crud/ 
│   │   ├── __init__.py 
│   │   ├── user.py 
│   ├── db/ 
│   │   ├── __init__.py 
│   │   ├── base.py 
│   │   ├── session.py 
│   ├── main.py 
│ 
├── tests/ 
│   ├── __init__.py 
│   ├── test_users.py 
│ 
├── requirements.txt 
└── README.md

Breaking Down the Components

1. app/api/: Where Your Routes Live

Organize all your routers by version (e.g., v1, v2) and then by feature (e.g., users, auth, products). This keeps your API modular and ready for future versioning.

# app/api/v1/endpoints/users.py 
 
from fastapi import APIRouter, Depends 
from app.schemas.user import UserOut 
from app.crud.user import get_user 
 
router = APIRouter() 
 
@router.get("/{user_id}", response_model=UserOut) 
def read_user(user_id: int): 
    return get_user(user_id)

2. app/core/: App-Wide Settings & Security

This includes your configuration logic, JWT security utilities, and any reusable constants.

# app/core/config.py 
 
from pydantic import BaseSettings 
 
class Settings(BaseSettings): 
    DATABASE_URL: str 
    SECRET_KEY: str 
    ALGORITHM: str = "HS256" 
 
    class Config: 
        env_file = ".env" 
 
settings = Settings()

3. app/models/ & app/schemas/: Your Database and Data Validation Layer

  • models: SQLAlchemy models for your DB tables.
  • schemas: Pydantic models for request and response validation.
# app/schemas/user.py 
 
from pydantic import BaseModel 
 
class UserOut(BaseModel): 
    id: int 
    username: str 
    email: str 
 
    class Config: 
        orm_mode = True

4. app/crud/: Business Logic and Database Access

Put all your query logic here to keep your endpoints thin and focused.

# app/crud/user.py 
 
from sqlalchemy.orm import Session 
from app.models.user import User 
 
def get_user(db: Session, user_id: int): 
    return db.query(User).filter(User.id == user_id).first()

5. app/db/: Database Session and Base

Everything related to initializing your SQLAlchemy engine and session management goes here.

# app/db/session.py 
 
from sqlalchemy import create_engine 
from sqlalchemy.orm import sessionmaker 
 
from app.core.config import settings 
 
engine = create_engine(settings.DATABASE_URL) 
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

6. app/main.py: The Entry Point

Mount your routers here and wire everything up.

# app/main.py 
 
from fastapi import FastAPI 
from app.api.v1.endpoints import users, auth 
 
app = FastAPI() 
 
app.include_router(users.router, prefix="/api/v1/users", tags=["Users"]) 
app.include_router(auth.router, prefix="/api/v1/auth", tags=["Auth"])

Don’t Skip the tests/ Folder

Testing is not optional in production-grade apps. Structure your test files to mirror your app folder. Use pytest, mock external dependencies, and aim for at least 80% coverage.


Pro Tips

  • Use Dependency Injection: FastAPI shines here. Inject DB sessions, user auth, etc. cleanly using Depends.
  • Environment Management: Use .env files + pydantic.BaseSettings.
  • Version your APIs from Day 1. It’s much harder to retrofit later.
  • Keep endpoints thin — push logic to crud/ and services/.
  • Dockerize your app for consistent development and deployment.

Final Thoughts

There’s no “one-size-fits-all” structure — but the key is modularity. Your FastAPI project should be easy to navigate, debug, and extend. A solid foundation today prevents pain tomorrow.

Start simple, but think big.

Happy coding


Did you find this helpful? Leave a 👏 or drop your thoughts in the comments — I’d love to hear how you structure your FastAPI apps!

Photo by Pedro Miranda on Unsplash