Week 5: Relationships, Transactions & Alembic

Build the next production-ready layer of your FastAPI service through clear concepts, a focused implementation and a practical exercise.

Module 4 of 12Week 5 of 15~3-4 HoursHands-on Exercise Included

By the end of this week, you'll be able to

  • One-to-many and many-to-many relationships
  • Loading strategies and the N+1 problem
  • Atomic transactions and Alembic migrations

1. One-to-many and many-to-many relationships

Start with the contract: make inputs, outputs and failure behavior explicit before adding infrastructure. This keeps the feature easy to reason about and gives tests a stable boundary.

2. Loading strategies and the N+1 problem

Apply the pattern through a small vertical slice. Keep framework wiring at the edge and business decisions in focused functions or services that can be tested without starting the whole application.

core example
class Project(Base):
    __tablename__ = "projects"
    id: Mapped[int] = mapped_column(primary_key=True)
    tasks: Mapped[list["Task"]] = relationship(back_populates="project")

stmt = select(Project).options(selectinload(Project.tasks))

3. Atomic transactions and Alembic migrations

Treat failure paths as part of the design. Add bounded resource usage, meaningful errors and a verification step so the behavior remains dependable under real production conditions.

4. Hands-on Exercise

Build the feature

Add projects and task ownership, generate and review an Alembic migration, then prove rollback keeps the database consistent when a multi-write operation fails.

Definition of done

  • The happy path works through the real HTTP boundary.
  • At least one failure path is handled and tested.
  • Configuration and secrets stay outside source code.
  • The README explains how to run and verify the result.

5. Knowledge Check

What problem does selectinload() solve?

Show answer

It loads related rows in a bounded extra query instead of triggering one query per parent row.