Mastering FastAPI: Organizing Bigger Applications with Multiple Files for a Seamless User Experience

FastAPI has rapidly become a popular choice among web developers for its simplicity, performance, and ease of use. As your application grows, maintaining a clean and organized codebase becomes crucial. In this blog post, we’ll explore best practices for structuring larger FastAPI applications using multiple files, ensuring a seamless user experience.

Why Organize Your FastAPI Application?

Organizing your FastAPI application into multiple files is essential for several reasons:

  • Readability: A well-structured codebase is easier to navigate and understand.
  • Maintainability: Isolated components reduce complexity, making it simpler to update and debug code.
  • Collaboration: A modular structure enables multiple developers to work on different parts of the application simultaneously.

Project Structure

A typical structure for a larger FastAPI application might look like this:


project_root/
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── routers/
│   │   ├── __init__.py
│   │   ├── users.py
│   │   ├── items.py
│   ├── models/
│   │   ├── __init__.py
│   │   ├── user.py
│   │   ├── item.py
│   ├── schemas/
│   │   ├── __init__.py
│   │   ├── user.py
│   │   ├── item.py
│   ├── databases.py
├── tests/
│   ├── __init__.py
│   ├── test_users.py
│   ├── test_items.py
├── requirements.txt

Each folder serves a specific purpose:

  • app/: Contains the main application files.
  • routers/: Holds the route logic (endpoints) split into different modules.
  • models/: Contains the database models.
  • schemas/: Defines the Pydantic models for data validation.
  • databases.py: Sets up the database connection.
  • tests/: Includes test cases for the application.

Setting Up the Main Application

Let’s start by setting up the main application file main.py:


from fastapi import FastAPI
from .routers import users, items

app = FastAPI()

app.include_router(users.router)
app.include_router(items.router)

This file initializes the FastAPI app and includes routers for users and items.

Creating Routers

Next, let’s create router files for our application. For example, here’s the users.py router:


from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from .. import models, schemas, databases

router = APIRouter(prefix="/users", tags=["users"])

@router.post("/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(databases.get_db)):
    db_user = models.User(name=user.name, email=user.email)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

This router handles user creation, using dependency injection for the database session.

Setting Up Models and Schemas

For database models, let’s define a simple user model in user.py:


from sqlalchemy import Column, Integer, String
from ..databases import Base

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    email = Column(String, unique=True, index=True)

And here’s a Pydantic schema for user validation in user.py under schemas/:


from pydantic import BaseModel

class UserBase(BaseModel):
    name: str
    email: str

class UserCreate(UserBase):
    pass

class User(UserBase):
    id: int

    class Config:
        orm_mode = True

These schemas ensure data validity and serialization.

Database Configuration

Finally, set up the database connection in databases.py:


from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"

engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

This configuration uses SQLite for simplicity but can be adapted for other databases.

Conclusion

In this post, we covered how to organize a larger FastAPI application into multiple files for better readability, maintainability, and collaboration. By structuring your application with clear separation of concerns, you can ensure a more scalable and manageable codebase. Start refactoring your FastAPI projects today to experience the benefits of a well-organized application!

Call to Action: Try reorganizing your existing FastAPI application following the structure outlined in this post. Share your experiences in the comments below!