Skip to content
Chapter 06 · 15 minutes

Dependencies and settings

Let FastAPI hand each route what it needs: a session, pagination, settings.

Goal

Write a dependency, reuse it everywhere, and read settings from the environment safely.

A dependency is a function FastAPI calls for you

From app/deps.py:

def get_session(request: Request) -> Iterator[Session]:
    """One database session per request, always closed afterwards."""
    with request.app.state.session_factory() as session:
        yield session


SessionDep = Annotated[Session, Depends(get_session)]

Any route that asks for session: SessionDep gets a fresh session. The code after yield runs when the request is finished, so the session is always closed, even after an error.

Dependencies can take parameters too

Pagination is the same everywhere, so it is written once:

def pagination(
    limit: Annotated[int, Query(ge=1, le=100, description="How many items to return")] = 20,
    offset: Annotated[int, Query(ge=0, description="How many items to skip")] = 0,
) -> Pagination:
    return Pagination(limit=limit, offset=offset)


PaginationDep = Annotated[Pagination, Depends(pagination)]

/books and /activity both use page: PaginationDep, and both show limit and offset in the docs with the same rules.

Settings from the environment

app/config.py uses pydantic-settings. Every field can be set with a SHELF_ variable or in a .env file:

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="SHELF_", env_file=".env", extra="ignore")

    environment: Literal["development", "test", "production"] = "development"
    database_url: str = "sqlite:///./shelf.db"
    api_key: SecretStr = SecretStr(DEV_API_KEY)

SecretStr prints as **********, so the key never ends up in a log by accident. You must call .get_secret_value() on purpose to read it.

cp .env.example .env    # then edit .env; it is git-ignored

Why this matters for tests

The app keeps its settings and session factory on app.state, and dependencies read them from there. A test builds an app with an in-memory database and a test key, and every route uses those without any code changes. You will see this in chapter 09.

Check it works

curl "http://127.0.0.1:8000/books?limit=500"   # 422: limit must be 100 or less

Exercise

Add a max_page_size setting (default 100) and make pagination refuse anything bigger. Hint: a dependency can itself depend on SettingsDep.