Почему Depends в FastAPI вызывается один раз: кэш внутри запроса

Маршрут получает пользователя напрямую и через проверку прав. В обеих ветках указана одна функция зависимости. Вы ожидаете два обращения к базе, но видите одно. Это штатное поведение: FastAPI переиспользует результат одинаковой зависимости в пределах одного запроса.

Кэш запроса не становится глобальным

Переиспользование помогает нескольким зависимостям работать с одним пользователем или одной Session. Результат живёт в текущем запросе. Следующий запрос снова вычислит зависимость. Это не кэш между пользователями и не способ хранить данные процесса. Если нужно кеширование каталога на минуту, проектируйте его отдельно с явным ключом и временем жизни.

use_cache=False позволяет потребителю получить новое вычисление вместо чтения уже сохранённого результата. Применяйте его, когда повторное чтение действительно имеет смысл: например, для изменяющегося состояния внутри одной операции. Для текущего пользователя и сессии базы обычное переиспользование чаще является желательным свойством.

Наблюдаем число вызовов на HTTP-запросе

Счётчик ниже делает результат видимым. На /cached две ветки используют один результат. На /fresh вторая ветка просит новый. Между запросами счётчик растёт, и это дополнительно показывает границу кэша. Нужны FastAPI и HTTPX.

Сохраните весь блок в файл и запустите Python 3.11+ в отдельном окружении с пакетами fastapi, httpx. Для моделей используется Pydantic v2; отдельный сервер для TestClient не нужен.

from typing import Annotated
from fastapi import FastAPI, Depends
from fastapi.testclient import TestClient

app = FastAPI()
calls = 0

def value():
    global calls
    calls += 1
    return calls

@app.get("/cached")
def cached(a: Annotated[int, Depends(value)], b: Annotated[int, Depends(value)]):
    return [a, b]

@app.get("/fresh")
def fresh(a: Annotated[int, Depends(value)],
          b: Annotated[int, Depends(value, use_cache=False)]):
    return [a, b]

with TestClient(app) as client:
    assert client.get("/cached").json() == [1, 1]
    assert client.get("/cached").json() == [2, 2]
    assert client.get("/fresh").json() == [3, 4]
assert calls == 4
print("dependency executions:", calls)

Какое поведение зафиксировать в рабочем API

Для сессии базы важно, чтобы транзакционная логика не получила случайно две независимые Session. Сохраните get_db одной функцией и переиспользуйте её в зависимостях. Две функции с одинаковым телом остаются разными зависимостями: сходство исходного кода не делает их одним ключом кэша.

Для проверки прав, наоборот, не считайте повторное выполнение доказательством безопасности. Правильность зависит от того, какие данные проверяются и кому разрешён ресурс. Кэшированная загрузка пользователя внутри запроса обычно корректна; устаревание данных между запросами решается на другой границе.

Почему счётчик не должен попасть в production

Глобальное число здесь только наблюдает учебный эксперимент. В нескольких процессах у каждого будет собственное значение, а параллельные запросы изменят порядок. Для рабочего наблюдения используйте журналы с идентификатором запроса или метрики вызовов, не превращая счётчик в источник бизнес-данных.

Если зависимость возвращает изменяемый объект, две ветки видят именно один результат. Изменение объекта в первой ветке может быть видно во второй. Предпочитайте не менять общий результат разбора настроек или параметров, а для ресурсов вроде Session учитывайте совместное использование явно. Это помогает читать граф зависимостей как порядок получения данных, а не как набор независимых вызовов.

Следующая практика

В открытом уроке о зависимостях вы увидите, как FastAPI передаёт результат Depends в маршрут. Продолжите экспериментом со счётчиком из статьи, чтобы отдельно понять переиспользование результата внутри запроса. Начните с бесплатного урока Depends.

Продолжите бесплатную программу FastAPI до базы, авторизации и тестов: граф зависимостей станет частью цельного backend. Сравнивайте время жизни пользователя, Session и настроек, а не применяйте одинаковый кэш ко всем данным. Откройте программу курса.

Продолжить на практике