FastAPI + SQLAlchemy 2 CRUD with dependency-injected sessions: working versions (tested 2026-10-15)
FastAPI routes backed by SQLAlchemy 2.x typed models, a per-request session dependency, and TestClient tests on SQLite.
passed2 passed · 0 failed2026-10-15 13:14 UTC- Status
- passed
- Last passed
- 2026-10-15 13:14 UTC
- Tests
- 2 / 2
- Language
- Python
- Runtime
- Python 3.13.16
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38740169402
The code
Exactly the files that passed. Your agent can pull them with get_recipe("fastapi-sqlalchemy-crud") (install), or see them on GitHub.
fastapi==0.143.1 sqlalchemy==2.1.4 httpx==0.28.1 pydantic==2.14.1 pytest==9.1.1
from collections.abc import Iterator
from fastapi import Depends, FastAPI, HTTPException
from pydantic import BaseModel, ConfigDict
from sqlalchemy import String, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column, sessionmaker
from sqlalchemy.pool import StaticPool
engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool)
SessionLocal = sessionmaker(engine, expire_on_commit=False)
class Base(DeclarativeBase):
pass
class Item(Base):
__tablename__ = "items"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100))
Base.metadata.create_all(engine)
class ItemIn(BaseModel):
name: str
class ItemOut(ItemIn):
model_config = ConfigDict(from_attributes=True)
id: int
def get_session() -> Iterator[Session]:
with SessionLocal() as session:
yield session
app = FastAPI()
@app.post("/items", response_model=ItemOut, status_code=201)
def create_item(data: ItemIn, db: Session = Depends(get_session)):
item = Item(name=data.name)
db.add(item)
db.commit()
return item
@app.get("/items/{item_id}", response_model=ItemOut)
def read_item(item_id: int, db: Session = Depends(get_session)):
item = db.get(Item, item_id)
if item is None:
raise HTTPException(404, "not found")
return item
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_create_and_read():
r = client.post("/items", json={"name": "widget"})
assert r.status_code == 201
item_id = r.json()["id"]
assert client.get(f"/items/{item_id}").json() == {"id": item_id, "name": "widget"}
def test_missing_and_invalid():
assert client.get("/items/9999").status_code == 404
assert client.post("/items", json={}).status_code == 422
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
Alembic autogenerate compare_metadata drift check against SQLite
Declare SQLAlchemy 2 models with a naming convention, then detect schema drift offline by diffing the live SQLite schema against the metadata with Alembic compare_metadata.
FastAPI with async SQLAlchemy 2 on aiosqlite, tested offline with per-test in-memory DBs
A FastAPI app using AsyncSession and async_sessionmaker on aiosqlite, tested with httpx.AsyncClient over ASGITransport, overriding the session dependency with a fresh in-memory database for each test.
SQLAlchemy 2 + Alembic programmatic migrations
Alembic migrations driven from Python (upgrade, downgrade, autogenerate check) against SQLite, with SQLAlchemy 2 models.
Async repository pattern with SQLAlchemy 2 AsyncSession on aiosqlite
A typed async repository over SQLAlchemy 2 AsyncSession with eager loading, foreign keys enforced on SQLite, and offline pytest-asyncio tests against an in-memory database.