
FastAPI
Creating APIs that scale requires proper architecture, error handling, and following best practices.
Organize code for maintainability:
api/
āāā main.py
āāā requirements.txt
āāā .env
āāā .env.example
āāā app/
ā āāā __init__.py
ā āāā config.py
ā āāā database.py
ā āāā models/
ā ā āāā __init__.py
ā ā āāā user.py
ā ā āāā item.py
ā āāā schemas/
ā ā āāā __init__.py
ā ā āāā user.py
ā ā āāā item.py
ā āāā api/
ā ā āāā __init__.py
ā ā āāā dependencies.py
ā ā āāā routes/
ā ā āāā __init__.py
ā ā āāā users.py
ā ā āāā items.py
ā āāā core/
ā āāā __init__.py
ā āāā security.py
ā āāā logging.py
āāā tests/
āāā __init__.py
āāā conftest.py
āāā test_items.py# app/config.py
from pydantic_settings import BaseSettings
from typing import Optional
class Settings(BaseSettings):
app_name: str = "My API"
app_version: str = "1.0.0"
debug: bool = False
# Database
database_url: str
db_pool_size: int = 20
db_max_overflow: int = 40
# Security
secret_key: str
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
# API
api_v1_str: str = "/api/v1"
allowed_hosts: list = ["*"]
class Config:
env_file = ".env"
case_sensitive = True
settings = Settings()# app/database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
from app.config import settings
engine = create_engine(
settings.database_url,
pool_size=settings.db_pool_size,
max_overflow=settings.db_max_overflow,
pool_pre_ping=True, # Test connections before using
echo=settings.debug
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_db() -> Session:
db = SessionLocal()
try:
yield db
finally:
db.close()# app/models/item.py
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
title = Column(String(255), nullable=False, index=True)
description = Column(String(1000))
price = Column(Float, nullable=False)
owner_id = Column(Integer, ForeignKey("users.id"))
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
owner = relationship("User", back_populates="items")
# app/schemas/item.py
from pydantic import BaseModel, Field
from datetime import datetime
from typing import Optional
class ItemBase(BaseModel):
title: str = Field(..., min_length=1, max_length=255)
description: Optional[str] = None
price: float = Field(..., gt=0)
class ItemCreate(ItemBase):
pass
class ItemUpdate(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = None
class Item(ItemBase):
id: int
owner_id: int
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True# app/api/routes/items.py
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from typing import List
import logging
from app.database import get_db
from app.models.item import Item as ItemModel
from app.schemas.item import Item, ItemCreate, ItemUpdate
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/items", tags=["items"])
@router.get("", response_model=List[Item])
async def list_items(
skip: int = Query(0, ge=0),
limit: int = Query(10, ge=1, le=100),
db: Session = Depends(get_db)
):
'''List items with pagination'''
items = db.query(ItemModel).offset(skip).limit(limit).all()
return items
@router.post("", response_model=Item, status_code=status.HTTP_201_CREATED)
async def create_item(
item: ItemCreate,
db: Session = Depends(get_db)
):
'''Create a new item'''
try:
db_item = ItemModel(**item.dict())
db.add(db_item)
db.commit()
db.refresh(db_item)
logger.info(f"Created item: {db_item.id}")
return db_item
except Exception as e:
db.rollback()
logger.error(f"Error creating item: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to create item"
)
@router.get("/{item_id}", response_model=Item)
async def get_item(item_id: int, db: Session = Depends(get_db)):
'''Get item by ID'''
item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
if not item:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Item not found"
)
return item
@router.put("/{item_id}", response_model=Item)
async def update_item(
item_id: int,
item: ItemUpdate,
db: Session = Depends(get_db)
):
'''Update an item'''
db_item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
if not db_item:
raise HTTPException(status_code=404, detail="Item not found")
update_data = item.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(db_item, field, value)
db.add(db_item)
db.commit()
db.refresh(db_item)
logger.info(f"Updated item: {item_id}")
return db_item
@router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int, db: Session = Depends(get_db)):
'''Delete an item'''
item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
if not item:
raise HTTPException(status_code=404, detail="Item not found")
db.delete(item)
db.commit()
logger.info(f"Deleted item: {item_id}")# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
import logging
from app.config import settings
from app.api.routes import items, users
logging.basicConfig(level=logging.INFO)
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
logger = logging.getLogger(__name__)
logger.info("Starting up")
yield
# Shutdown
logger.info("Shutting down")
app = FastAPI(
title=settings.app_name,
version=settings.app_version,
lifespan=lifespan
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=settings.allowed_hosts,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Routes
app.include_router(items.router)
app.include_router(users.router)
@app.get("/health")
async def health_check():
return {"status": "healthy"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=settings.debug,
workers=1 if settings.debug else 4
)Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep ⢠Build Real ⢠Verify Skills ⢠Launch Forward