FastAPI: как повысить читаемость кода с Depends
Лучшие практики FastAPI для Depends: именование параметров, возврат Pydantic-моделей TokenData или username, композиция зависимостей. Примеры для обновления токена, архитектура как нормализация БД, безопасность и тесты.
Как повысить читаемость кода при использовании Depends в FastAPI? На примере:
@app.post('/refresh')
@limiter.limit("10/minute")
async def refresh(request: Request, token: str = Depends(check_refresh_token)):
access_token = create_access_token({"sub": token})
pass
Зависимость:
def check_refresh_token(token: str = Depends(oauth2_scheme)):
*логика функции*
return payload.get("sub")
Функция check_refresh_token возвращает username (payload.get("sub")), но в эндпоинте этот параметр назван token, из‑за чего возникает путаница. Конечно, можно в эндпоинте писать username = token, но хотелось бы понять лучшие практики:
- Как правильно именовать параметр в эндпоинте —
tokenилиusername? - Что лучше возвращать из функции, используемой в
Depends: толькоusername, весьpayload, Pydantic‑модель или специальный объект (напримерTokenData)? - Какие подходы и правила повышают читаемость и поддерживаемость кода при работе с зависимостями в FastAPI? Есть ли архитектурные признаки (аналогии с нормализацией БД), которые подскажут, когда стоит возвращать минимальные данные, а когда — полные объекты?
Какие рекомендации по именованию и структуре возвращаемых значений вы бы посоветовали в таких ситуациях?
Лучше именовать параметр в эндпоинте так, чтобы он отражал именно то, что возвращает зависимость — например username или current_user; в FastAPI при использовании Depends это значительно повышает читаемость кода. Возвращайте из зависимости минимально достаточную и типизированную структуру (строка для одного поля или Pydantic‑модель TokenData/User), а сложную логику разделяйте на мелкие зависимости — проверка токена, декодирование payload, загрузка пользователя. Такие правила (именование, явные аннотации типов, композиция зависимостей) делают код модульным, безопасным и удобным для тестов и автодополнения в IDE.
Содержание
- Именование параметров в FastAPI и Depends
- Что возвращать из зависимости — строка, payload или Pydantic‑модель?
- Архитектурные принципы и аналогия с нормализацией БД
- Практические шаблоны и примеры кода
- Безопасность, обработка ошибок и тестируемость
- Контрольный список: рекомендации по именованию и структуре
- Источники
- Заключение
Именование параметров в FastAPI и Depends
Имена параметров — маленькая, но очень важная вещь. Если ваша зависимость check_refresh_token возвращает username (строку), то в эндпоинте параметр должен называться username или current_username, а не token. Это исключает путаницу и делает код само‑документируемым.
Почему это важно:
- Имя параметра сообщает сразу, что именно вы используете в теле функции (не нужно читать реализацию зависимости).
- IDE и статические анализаторы лучше подсказывают типы, особенно при явных аннотациях.
- OpenAPI‑документация и читаемость для коллег улучшаются.
Примеры имен:
- dependency возвращает username (str) → endpoint:
username: str = Depends(check_refresh_token)илиcurrent_username: str = Depends(check_refresh_token). - dependency возвращает Pydantic модель TokenData → endpoint:
token_data: TokenData = Depends(check_refresh_token). - dependency возвращает объект пользователя → endpoint:
current_user: User = Depends(get_current_user).
См. также рекомендации по именованию параметров в официальной документации FastAPI: Tutorial — Path Parameters.
Что возвращать из зависимости — строка, payload или Pydantic‑модель?
Есть несколько практик, и выбор зависит от контекста. Главный принцип — возвращать минимально достаточные и типизированные данные.
Варианты и когда их использовать:
- Простая строка (
str) — когда вам нужен только username. Простой, лёгкий, очевидный. - Словарь/raw
payload— удобно быстро, но плохо для читаемости и автодополнения; риск опечаток. - Pydantic‑модель (например
TokenData) — рекомендуемая практика: валидация, автодополнение, документация. Хорошо, если токен содержит несколько значимых полей (username, scopes, exp). - Полный
User(Pydantic модель) — возвращать, если endpoint реально работает с объектом пользователя (поля профиля, роли и т.п.). Чаще получают пользователя отдельной зависимостью, чтобы разделить обязанности.
FastAPI сама поощряет использование Pydantic для структуры входных/выходных данных — см. Request Body and Fields и пример работы с OAuth2/JWT: OAuth2 with JWT.
Рекомендация:
- Для проверки токена и извлечения идентификатора — возвращайте
TokenDataс явно объявленными полями. - Для доступа к данным из БД — создавайте отдельную зависимость
get_current_user(token_data: TokenData = Depends(...)), которая возвращаетUserиз БД.
Архитектурные принципы и аналогия с нормализацией БД
Подход «разделяй и властвуй» здесь работает отлично. Представьте зависимости как уровни нормализации:
- Первый уровень (как первичный ключ): проверка подписи токена и извлечение минимального идентификатора (например,
usernameилиuser_id). - Второй уровень (как внешние ключи и связи): загрузка дополнительной информации (пользователя, прав) по этому идентификатору.
Преимущества композиции:
- Меньше дублирования логики.
- Легче тестировать отдельные шаги.
- Можно переиспользовать мелкие зависимости в разных цепочках.
Когда возвращать минимальные данные:
- Если большая часть эндпоинтов использует лишь идентификатор (например, только для логирования или простого создания токена) — возвращайте
usernameилиid.
Когда возвращать полный объект: - Если большинство эндпоинтов сразу нуждаются в полном
User(поля профиля, роли и т.д.), имеет смысл, чтобы верхний слой зависимости сразу возвращалUser(чтобы не делать N запросов к БД).
Оптимизация: если и проверка токена, и загрузка пользователя выполняются часто вместе, объединённая зависимость уменьшит количество вызовов БД; но это снижает гибкость. Балансируйте читаемость/производительность по потребностям проекта.
Дополнительные возможности описаны в разделе про продвинутые зависимости: Advanced Dependencies.
Практические шаблоны и примеры кода
- Минимально: зависимость возвращает строку (username)
from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def check_refresh_token(token: str = Depends(oauth2_scheme)) -> str:
payload = decode_jwt(token) # ваша логика декодирования
username = payload.get("sub")
if not username:
raise HTTPException(status_code=401, detail="Invalid refresh token")
return username
@app.post("/refresh")
@limiter.limit("10/minute")
async def refresh(username: str = Depends(check_refresh_token)):
access_token = create_access_token({"sub": username})
return {"access_token": access_token}
- Возврат Pydantic‑модели
TokenData(рекомендуется, если нужны дополнительные поля)
from pydantic import BaseModel
class TokenData(BaseModel):
username: str
scopes: list[str] = []
exp: int | None = None
def check_refresh_token(token: str = Depends(oauth2_scheme)) -> TokenData:
payload = decode_jwt(token)
return TokenData(username=payload.get("sub"), scopes=payload.get("scopes", []))
@app.post("/refresh")
async def refresh(token_data: TokenData = Depends(check_refresh_token)):
access_token = create_access_token({"sub": token_data.username})
return {"access_token": access_token}
- Композиция: проверка токена → загрузка пользователя из БД
async def get_token_data(token: str = Depends(oauth2_scheme)) -> TokenData:
return TokenData(...)
async def get_current_user(token_data: TokenData = Depends(get_token_data), db=Depends(get_db)) -> User:
user = await db.users.get_by_username(token_data.username)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return User.from_orm(user)
@app.post("/refresh")
async def refresh(current_user: User = Depends(get_current_user)):
access_token = create_access_token({"sub": current_user.username})
return {"access_token": access_token}
Эти шаблоны соответствуют рекомендациям по безопасности и модульности, приведённым в OAuth2 с JWT.
Безопасность, обработка ошибок и тестируемость
Несколько практических пометок:
- Всегда проверяйте подпись токена, срок действия и тип (access vs refresh).
- Не возвращайте из зависимости чувствительные поля (
password_hash, секреты). - Для ошибок используйте
HTTPExceptionс корректным кодом и заголовкомWWW-Authenticate, если нужно. - Пишите юнит‑тесты для зависимости как для обычной функции — их удобно тестировать отдельно, без запуска приложения.
- Если в нескольких местах вы загружаете пользователя, подумайте о кэшировании на время запроса (request-scoped cache) или использовании
Dependsдля единого вызова.
Документация FastAPI по безопасности содержит полезные примеры: Security — FastAPI.
Контрольный список: рекомендации по именованию и структуре
- Именуйте параметр в эндпоинте согласно возвращаемому значению:
username,token_data,current_user. - Давайте зависимостям говорящие имена:
verify_refresh_token,get_token_data,get_current_user. - Аннотируйте возвращаемый тип (строка,
TokenData,User) — это помогает IDE и тестам. - Возвращайте Pydantic‑модель вместо сырых dict, если структура не тривиальна.
- Разделяйте обязанности: валидация токена ≠ загрузка пользователя.
- Используйте классовые зависимости или
yieldдля управления ресурсами, если нужно (см. Advanced Dependencies). - Не возвращайте лишние или чувствительные данные.
- Документируйте зависимость кратким docstring — быстро помогает понять, что она делает.
- Покройте зависимости тестами: отдельно проверяйте декодирование, отдельный тест для загрузки пользователя.
- При многократных запросах к БД внутри цепочки зависимостей — продумайте кэширование запроса по идентификатору.
Источники
- https://fastapi.tiangolo.com/tutorial/dependencies/
- https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/
- https://fastapi.tiangolo.com/tutorial/body-fields/
- https://fastapi.tiangolo.com/tutorial/path-params/
- https://fastapi.tiangolo.com/tutorial/bigger-applications/
- https://fastapi.tiangolo.com/advanced/advanced-dependencies/
- https://fastapi.tiangolo.com/tutorial/security/
- https://www.freecodecamp.org/news/how-to-build-a-production-ready-api-with-fastapi-and-postgresql/
- https://www.freecodecamp.org/news/how-to-use-fastapi-dependencies-to-build-a-secure-api/
- https://stackoverflow.com/questions/60341999/fastapi-how-to-return-a-pydantic-model-from-a-dependency
Заключение
Итог: в FastAPI при использовании Depends именуйте параметры так, чтобы они отражали возвращаемое значение (username, token_data, current_user), возвращайте минимально достаточную и типизированную структуру (строка или Pydantic‑модель TokenData/User) и разделяйте обязанности между зависимостями (валидировать токен отдельно, загружать пользователя отдельно). Такой подход улучшает читаемость кода, упрощает тестирование и поддерживает безопасность при работе с токенами и обновлением токенов.