[{"data":1,"prerenderedAt":88},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Fdatabase\u002Fsqlalchemy":3},{"page":4,"siblings":79,"blog":70},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":69,"related":70,"seo":71,"seoDescription":72,"stem":73,"subtopic":74,"topic":75,"topicSlug":76,"updated":77,"__hash__":78},"qa\u002Ffastapi\u002Fdatabase\u002Fsqlalchemy.md","Sqlalchemy",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","FastAPI","fastapi",{},true,1,"\u002Ffastapi\u002Fdatabase\u002Fsqlalchemy",[23,28,32,36,40,44,49,53,57,61,65],{"id":24,"difficulty":25,"q":26,"a":27},"sqlalchemy-setup","easy","How do you set up SQLAlchemy with FastAPI for synchronous use?","```python\nfrom sqlalchemy import create_engine\nfrom sqlalchemy.orm import sessionmaker, DeclarativeBase\n\nDATABASE_URL = \"postgresql:\u002F\u002Fuser:pass@localhost\u002Fmydb\"\n\nengine = create_engine(DATABASE_URL, pool_size=10, max_overflow=20)\nSessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)\n\nclass Base(DeclarativeBase):\n    pass\n```\n\nUse a yield dependency to provide a session per request:\n```python\nfrom fastapi import Depends\nfrom sqlalchemy.orm import Session\n\ndef get_db():\n    db = SessionLocal()\n    try:\n        yield db\n    finally:\n        db.close()\n\n@app.get(\"\u002Fusers\")\ndef list_users(db: Session = Depends(get_db)):\n    return db.query(User).all()\n```\n\nRule of thumb: always use a yield dep for DB sessions — the `finally` block\nguarantees `db.close()` runs even if the handler raises.\n",{"id":29,"difficulty":25,"q":30,"a":31},"orm-model","How do you define a SQLAlchemy ORM model and map it to a Pydantic response model?","```python\nfrom sqlalchemy import Column, Integer, String\nfrom sqlalchemy.orm import DeclarativeBase\nfrom pydantic import BaseModel, ConfigDict\n\nclass Base(DeclarativeBase):\n    pass\n\n# ORM model (maps to the DB table)\nclass User(Base):\n    __tablename__ = \"users\"\n    id = Column(Integer, primary_key=True)\n    name = Column(String(100), nullable=False)\n    email = Column(String(200), unique=True, nullable=False)\n\n# Pydantic output schema\nclass UserOut(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n    id: int\n    name: str\n    email: str\n```\n\n`from_attributes=True` lets Pydantic read ORM attributes instead of dict keys.\n\nRule of thumb: keep ORM models and Pydantic schemas in separate files — they\nserve different purposes and change for different reasons.\n",{"id":33,"difficulty":14,"q":34,"a":35},"session-commit-rollback","When should you call `db.commit()` and `db.rollback()` in a FastAPI route?","Commit after all writes in the handler succeed; rollback on exception:\n\n```python\ndef get_db():\n    db = SessionLocal()\n    try:\n        yield db\n    except Exception:\n        db.rollback()\n        raise\n    finally:\n        db.close()\n\n@app.post(\"\u002Fusers\")\ndef create_user(user: UserCreate, db: Session = Depends(get_db)):\n    db_user = User(name=user.name, email=user.email)\n    db.add(db_user)\n    db.commit()           # persist to DB\n    db.refresh(db_user)   # reload auto-generated fields (id, created_at)\n    return UserOut.model_validate(db_user)\n```\n\n`autocommit=False` (the default) means writes are buffered until `commit()`.\nReads don't need a commit.\n\nRule of thumb: commit as late as possible (after all writes succeed); never\ncommit in a loop — batch all changes and commit once.\n",{"id":37,"difficulty":14,"q":38,"a":39},"querying-patterns","What are the main SQLAlchemy query patterns used in FastAPI routes?","**ORM-style queries** (SQLAlchemy 2.x preferred):\n```python\nfrom sqlalchemy import select\n\n# Single object\nuser = db.get(User, user_id)                       # by primary key\n\n# Filtered list\nstmt = select(User).where(User.is_active == True)\nusers = db.scalars(stmt).all()\n\n# Pagination\nstmt = select(User).offset(skip).limit(limit)\nusers = db.scalars(stmt).all()\n\n# Count\nfrom sqlalchemy import func\ncount = db.scalar(select(func.count()).select_from(User))\n```\n\n**Legacy query API** (still works, being phased out):\n```python\nuser = db.query(User).filter(User.id == user_id).first()\n```\n\nRule of thumb: prefer `select()` + `db.scalars()` (SQLAlchemy 2.x style) over\n`db.query()` — it's the modern API and works with both sync and async.\n",{"id":41,"difficulty":14,"q":42,"a":43},"relationships","How do you handle SQLAlchemy relationships in FastAPI responses?","Load related objects eagerly to avoid N+1 queries:\n\n```python\nfrom sqlalchemy.orm import relationship, selectinload\n\nclass User(Base):\n    __tablename__ = \"users\"\n    id = Column(Integer, primary_key=True)\n    posts = relationship(\"Post\", back_populates=\"author\")\n\n# Eager load with selectinload\nstmt = select(User).options(selectinload(User.posts)).where(User.id == user_id)\nuser = db.scalars(stmt).first()\n```\n\nPydantic schema with nested relationship:\n```python\nclass PostOut(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n    id: int\n    title: str\n\nclass UserWithPosts(BaseModel):\n    model_config = ConfigDict(from_attributes=True)\n    id: int\n    name: str\n    posts: list[PostOut]\n```\n\nRule of thumb: always eager-load relationships you plan to serialise —\nlazy loading in a closed session causes `DetachedInstanceError`.\n",{"id":45,"difficulty":46,"q":47,"a":48},"n-plus-one","hard","What is the N+1 query problem and how do you avoid it in FastAPI?","The **N+1 problem** occurs when you load N parent rows and then issue 1 query\nper parent to load its children — N+1 queries total.\n\n```python\n# N+1 — 1 query for users + 1 per user for posts\nusers = db.scalars(select(User)).all()\nfor user in users:\n    print(user.posts)   # lazy load triggers a new query each time!\n```\n\nFix with eager loading:\n```python\n# 2 queries total: one for users, one for all their posts\nusers = db.scalars(\n    select(User).options(selectinload(User.posts))\n).all()\n\n# Or joined load (single query with JOIN)\nusers = db.scalars(\n    select(User).options(joinedload(User.posts))\n).all()\n```\n\n`selectinload` is better for one-to-many (avoids duplicated parent rows);\n`joinedload` is better for many-to-one or when the child set is small.\n\nRule of thumb: any serialised response that includes a relationship must use\n`selectinload` or `joinedload` — never rely on lazy loading in API handlers.\n",{"id":50,"difficulty":14,"q":51,"a":52},"db-session-scope","What is the correct scope for a SQLAlchemy session in FastAPI?","**One session per request**. The standard `get_db` yield dependency provides\nexactly this — a session is created at the start of the request and closed\n(and rolled back on error) at the end.\n\n```python\n# ✅ One session per request\ndef get_db():\n    db = SessionLocal()\n    try:\n        yield db\n    finally:\n        db.close()\n```\n\nAnti-patterns:\n- **Global session**: shared across requests → race conditions.\n- **Session per DB call**: loses transaction semantics; can't roll back multiple writes together.\n- **Thread-local session outside a request context**: leaks sessions.\n\nRule of thumb: bind the session to the HTTP request lifetime via a yield dep —\none request = one transaction = one session.\n",{"id":54,"difficulty":14,"q":55,"a":56},"sqlalchemy-fastapi-pattern","What is the recommended project structure for SQLAlchemy + FastAPI?","```\napp\u002F\n├── db\u002F\n│   ├── base.py          # DeclarativeBase\n│   ├── session.py       # engine, SessionLocal, get_db\n│   └── models\u002F\n│       ├── user.py      # SQLAlchemy ORM models\n│       └── order.py\n├── schemas\u002F             # Pydantic models (input\u002Foutput DTOs)\n│   ├── user.py\n│   └── order.py\n├── crud\u002F                # DB operations (no FastAPI imports)\n│   ├── user.py\n│   └── order.py\n└── routers\u002F\n    ├── users.py         # thin — calls CRUD, returns schema\n    └── orders.py\n```\n\nCRUD functions are plain Python:\n```python\n# crud\u002Fuser.py\ndef get_user(db: Session, user_id: int) -> User | None:\n    return db.get(User, user_id)\n```\n\nRule of thumb: keep DB logic in `crud\u002F`, keep HTTP logic in `routers\u002F` —\nit makes CRUD functions unit-testable without standing up an ASGI server.\n",{"id":58,"difficulty":46,"q":59,"a":60},"connection-pool","How does SQLAlchemy's connection pool interact with a multi-worker FastAPI deployment?","Each **Uvicorn worker process** gets its own SQLAlchemy engine and connection pool.\nWith 4 workers and `pool_size=10`, you have 40 total connections to the DB.\n\n```python\nengine = create_engine(\n    DATABASE_URL,\n    pool_size=5,        # connections kept open\n    max_overflow=10,    # burst connections allowed\n    pool_timeout=30,    # wait time before \"too many connections\" error\n    pool_recycle=1800,  # recycle connections every 30 min (avoids stale)\n)\n```\n\nFor containers \u002F Kubernetes where the worker count varies, use **PgBouncer**\nas a connection pooler in front of PostgreSQL to cap total connections\nregardless of pod count.\n\nRule of thumb: set `pool_size` conservatively (2-5 per worker); use PgBouncer\nin transaction-pooling mode for high-concurrency deployments.\n",{"id":62,"difficulty":25,"q":63,"a":64},"create-tables","How do you create database tables from SQLAlchemy models in FastAPI?","Call `Base.metadata.create_all(engine)` — typically in the `lifespan` function\nor a startup script:\n\n```python\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    Base.metadata.create_all(bind=engine)   # creates tables if they don't exist\n    yield\n\napp = FastAPI(lifespan=lifespan)\n```\n\nFor production, **never** use `create_all` at startup — use **Alembic** migrations\ninstead. `create_all` in production risks schema drift and doesn't handle alterations.\n\nRule of thumb: use `create_all` only for local dev and test databases; use\nAlembic for staging and production schema changes.\n",{"id":66,"difficulty":14,"q":67,"a":68},"soft-delete","How do you implement soft deletes in a SQLAlchemy + FastAPI application?","Add a `deleted_at` timestamp column and filter it out in queries:\n\n```python\nfrom datetime import datetime\nfrom sqlalchemy import Column, DateTime\nfrom sqlalchemy.sql import expression\n\nclass User(Base):\n    __tablename__ = \"users\"\n    id = Column(Integer, primary_key=True)\n    name = Column(String)\n    deleted_at = Column(DateTime, nullable=True, default=None)\n\n    @property\n    def is_deleted(self) -> bool:\n        return self.deleted_at is not None\n\n# Query only active records\nstmt = select(User).where(User.deleted_at.is_(None))\n\n# Soft delete\nuser.deleted_at = datetime.utcnow()\ndb.commit()\n```\n\nRule of thumb: always filter `WHERE deleted_at IS NULL` in all list queries —\nadd a SQLAlchemy event listener or a custom query class to enforce this globally.\n",11,null,{"description":11},"FastAPI SQLAlchemy interview questions — sync session setup, get_db dependency, ORM models, querying, relationships and Pydantic integration.","fastapi\u002Fdatabase\u002Fsqlalchemy","SQLAlchemy (Sync)","Database Integration","database","2026-06-20","GllAMcQpIv65eU2Qj4npgIWT77-UhwQshnmrAjK85hY",[80,81,84],{"subtopic":74,"path":21,"order":20},{"subtopic":82,"path":83,"order":12},"Async Database","\u002Ffastapi\u002Fdatabase\u002Fasync-db",{"subtopic":85,"path":86,"order":87},"Alembic Migrations","\u002Ffastapi\u002Fdatabase\u002Fmigrations",3,1782244113256]