How do you set up SQLAlchemy with FastAPI?
Setting up SQLAlchemy with FastAPI involves creating an engine, a session factory, and a declarative base. Unlike Flask-SQLAlchemy, FastAPI uses plain SQLAlchemy directly -- there is no official FastAPI-SQLAlchemy extension.
Steps:
- Create the engine with
create_engine()pointing to your database URL - Create a session factory with
sessionmaker()bound to the engine - Define a declarative base using
DeclarativeBase(SQLAlchemy 2.0 style) - Create a dependency that yields a session per request
- Create tables during application startup using lifespan
SQLAlchemy 2.0 style:
SQLAlchemy 2.0 uses DeclarativeBase instead of declarative_base(), and Mapped/mapped_column instead of Column. This gives you better type hints and IDE support.
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(bind=engine)
class Base(DeclarativeBase): pass
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/items")
def read_items(db: Session = Depends(get_db)):
return db.query(Item).all()
from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, String
from sqlalchemy.orm import (
DeclarativeBase, Mapped, mapped_column,
sessionmaker, Session
)
from contextlib import asynccontextmanager
from typing import Annotated
# Database setup
DATABASE_URL = "sqlite:///./app.db"
engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False} # SQLite only
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# SQLAlchemy 2.0 declarative base
class Base(DeclarativeBase):
pass
# Model
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100))
email: Mapped[str] = mapped_column(String(255), unique=True)
# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
DbDep = Annotated[Session, Depends(get_db)]
# Lifespan to create tables on startup
@asynccontextmanager
async def lifespan(app: FastAPI):
Base.metadata.create_all(bind=engine)
yield
app = FastAPI(lifespan=lifespan)
@app.get("/users")
def list_users(db: DbDep):
return db.query(User).all()
- •FastAPI uses plain SQLAlchemy -- no wrapper extension needed
- •Use DeclarativeBase and Mapped/mapped_column for SQLAlchemy 2.0 style
- •check_same_thread=False is required only for SQLite
- •Use lifespan context manager (not deprecated @app.on_event) to create tables at startup
- •Forgetting check_same_thread=False for SQLite causes threading errors
- •autoflush=True (the default) can cause unexpected queries -- set autoflush=False explicitly
- •create_all() does not update existing tables -- use Alembic for migrations
- •The session must always be closed, even on exceptions -- yield in a try/finally handles this