Breaking down the project into smaller, independent modules is the cornerstone of a well - structured FastAPI project. Each module should have a single responsibility, such as handling authentication, database operations, or specific business logic. This makes the code easier to understand, test, and maintain.
Separate different aspects of the application, like the API layer, business logic layer, and data access layer. This ensures that changes in one layer don’t affect the others, making the codebase more flexible and resilient.
FastAPI has built - in support for dependency injection. It allows you to manage and share dependencies across different parts of the application easily. Dependencies can be functions, classes, or other callables that provide a specific service.
A common directory structure for a large FastAPI project is as follows:
project_name/
├── app/
│ ├── api/
│ │ ├── v1/
│ │ │ ├── endpoints/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── users.py
│ │ │ │ ├── items.py
│ │ │ ├── __init__.py
│ │ ├── __init__.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── security.py
│ ├── db/
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── session.py
│ ├── services/
│ │ ├── __init__.py
│ │ ├── user_service.py
│ │ ├── item_service.py
│ ├── main.py
├── tests/
│ ├── __init__.py
│ ├── test_users.py
│ ├── test_items.py
├── requirements.txt
Use FastAPI’s router to organize your API endpoints. Routers allow you to group related endpoints together and manage them more efficiently.
# app/api/v1/endpoints/users.py
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/")
def read_users():
return {"message": "Read all users"}
Use an ORM (Object - Relational Mapping) like SQLAlchemy to interact with databases. Create database models, sessions, and manage database operations in the db
module.
# app/db/models.py
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
Implement a global error handler to catch and handle exceptions consistently across the application.
# app/main.py
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
return JSONResponse(status_code=500, content={"message": str(exc)})
Set up proper logging to track the application’s behavior and diagnose issues. You can use Python’s built - in logging
module.
# app/core/config.py
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
Use environment variables to manage configuration settings such as database connection strings, API keys, etc. You can use the python - decouple
library to read environment variables.
# app/core/config.py
from decouple import config
DATABASE_URL = config('DATABASE_URL')
Write unit tests and integration tests for your application. Use testing frameworks like pytest
and FastAPI’s test client to test your API endpoints.
# tests/test_users.py
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_read_users():
response = client.get("/api/v1/users/")
assert response.status_code == 200
Implement security measures such as authentication and authorization. Use FastAPI’s built - in security utilities and libraries like passlib
and pyjwt
for password hashing and JWT authentication.
# app/core/security.py
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
Use FastAPI’s automatic documentation generation features. FastAPI generates Swagger UI and ReDoc documentation for your API endpoints out of the box. You can also add additional documentation to your endpoints using docstrings.
# app/api/v1/endpoints/users.py
from fastapi import APIRouter
router = APIRouter()
@router.get("/users/", summary="Read all users", description="This endpoint returns a list of all users.")
def read_users():
return {"message": "Read all users"}
# app/main.py
from fastapi import FastAPI
from app.api.v1.endpoints.users import router as user_router
app = FastAPI()
app.include_router(user_router, prefix="/api/v1")
@app.get("/")
def read_root():
return {"Hello": "World"}
# app/db/session.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from app.core.config import DATABASE_URL
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Structuring a large FastAPI project is essential for its long - term success. By following the fundamental concepts, usage methods, common practices, and best practices outlined in this blog, you can create a scalable, maintainable, and secure FastAPI application. Remember to break down your project into modular components, separate concerns, and write comprehensive tests. With these practices in place, you’ll be well - equipped to handle the complexity of a large - scale FastAPI project.