Короткий ответ
Зависимость пишется как генератор с yield: код до yield открывает сессию, код после yield (в блоке finally) закрывает её независимо от того, завершился ли обработчик нормально или с исключением.
Как это работает подробнее
FastAPI поддерживает зависимости-генераторы: всё, что до yield, выполняется перед вызовом эндпоинта, значение после yield передаётся как аргумент, а код после yield выполняется после завершения обработки запроса — причём FastAPI гарантирует вызов этой части даже если эндпоинт выбросил исключение, оборачивая её в try/finally.
Пример кода
example.py
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from .database import SessionLocal
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users/{user_id}")
def read_user(user_id: int, db: Session = Depends(get_db)):
return db.get(User, user_id)
# Даже если read_user выбросит исключение, db.close() выполнится
# благодаря finally внутри зависимости get_db.Что использовать на практике
- Практика. Такой паттерн — стандартный способ управления ресурсами на время запроса (сессия БД, соединение с внешним сервисом, блокировка). Важно использовать именно
try/finallyвнутри зависимости, а не надеяться, что код послеyieldвыполнится «сам по себе» без исключений.
Подводные камни
- Риск. Пишут код закрытия ресурса после
yieldбезtry/finally, из-за чего при исключении в обработчике соединение не закрывается и со временем исчерпывается пул соединений; открывают сессию с областью видимости шире, чем один запрос (например, на уровне модуля), что приводит к расшариванию незакоммиченных изменений между независимыми запросами.