FastAPI + SQLAlchemy
Connect FastAPI to a database with SQLAlchemy ORM, sessions, and async support
Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.
UpgradeKnowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
SQLAlchemy is the standard Python ORM for working with relational databases. FastAPI's Depends() makes it clean to manage database sessions.
Setup:
python from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, Session DATABASE_URL = "postgresql://user:pass@localhost/mydb" engine = create_engine(DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base()
Model definition:
python class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) email = Column(String, unique=True, index=True, nullable=False) name = Column(String, nullable=False) is_active = Column(Boolean, default=True)
Session dependency — the core pattern:
python def get_db(): db = SessionLocal() try: yield db # route runs here with the db session finally: db.close() # always closes, even on error @app.get("/users/{user_id}") def get_user(user_id: int, db: Session = Depends(get_db)): user = db.query(User).filter(User.id == user_id).first() if not user: raise HTTPException(status_code=404, detail="User not found") return user
CRUD operations:
python # Create new_user = User(email="alice@example.com", name="Alice") db.add(new_user) db.commit() db.refresh(new_user) # load auto-generated id # Read users = db.query(User).filter(User.is_active == True).all() user = db.query(User).filter(User.email == email).first() # Update user.name = "Alice Smith" db.commit() # Delete db.delete(user) db.commit()
Pydantic schemas for request/response (separate from ORM models):
python from pydantic import BaseModel class UserCreate(BaseModel): email: str name: str class UserResponse(BaseModel): id: int email: str name: str is_active: bool class Config: from_attributes = True # allows reading from ORM model attributes @app.post("/users", response_model=UserResponse) def create_user(user_in: UserCreate, db: Session = Depends(get_db)): user = User(**user_in.model_dump()) db.add(user) db.commit() db.refresh(user) return user `` Always use separate Pydantic schemas for API input/output — never expose your ORM model directly (it can leak fields like passwords or internal IDs).
Examples
One-to-many relationship: User → Posts
relationships() lets you navigate between models in Python; joinedload() avoids the N+1 query problem
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
email = Column(String, unique=True, nullable=False)
posts = relationship("Post", back_populates="author")
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
author = relationship("User", back_populates="posts")
# Query with join (avoids N+1)
@app.get("/users/{user_id}/posts")
def get_user_posts(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).options(
joinedload(User.posts) # single SQL JOIN, not N queries
).filter(User.id == user_id).first()
return user.postsAsync SQLAlchemy with FastAPI
Async SQLAlchemy uses asyncpg driver and select() syntax instead of query(); needed when your routes are async
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/mydb"
engine = create_async_engine(DATABASE_URL)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession,
expire_on_commit=False)
async def get_db():
async with AsyncSessionLocal() as db:
yield db
@app.get("/users/{user_id}")
async def get_user(user_id: int, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if not user:
raise HTTPException(404, "Not found")
return userHow well did you understand this?