fastapi-cache-di
Speed up FastAPI startup by caching its dependency-tree introspection.
At startup, FastAPI re-introspects every route's dependency tree with no caching,
so dependencies shared across many routes (auth checks, DB handles, role guards …)
are parsed from scratch again and again. fastapi-cache-di memoizes that work for
the duration of route loading, turning O(routes × tree-depth) into
O(routes + unique-deps), then restores FastAPI's originals.
Install
pip install fastapi-cache-di
# or
uv add fastapi-cache-di
Usage
Wrap the code that loads your routes with the context manager:
from fastapi import APIRouter, FastAPI
from fastapi_cache_di import fastapi_deps_cache
app = FastAPI()
users = APIRouter()
orders = APIRouter()
billing = APIRouter()
with fastapi_deps_cache(warm_up=app):
# Registering routes introspects each dependency tree; caching is active here.
app.include_router(users)
app.include_router(orders)
app.include_router(billing)
# included routes built, originals restored here; cache memory freed
warm_up=app matters from FastAPI 0.137: include_router no longer builds the
included routes, FastAPI builds them on the first request through each included
router (or the first /openapi.json), after the block has exited and without
the cache. warm_up builds them when the block exits normally, while the cache
is still active. It has no effect before FastAPI 0.137.2, and does not cover
routes added after the block, mounted sub-applications or frontend() routes.
Manual patch/unpatch, sharing and inspecting the cache, and the cache-key details are covered in the documentation.
Calling loop-safe dependencies inline
At request time, FastAPI calls every dependency that is neither a coroutine
nor a generator through the threadpool: every Depends(SomeClass) and every
plain def dependency costs a round trip to a worker thread, often far more
than a class whose __init__ only stores its arguments.
patch_fastapi_run_in_threadpool() lets such dependencies run directly on the
event loop. It is opt-in, because the library cannot tell whether a call blocks:
loop_safemarks one sync function or class (it returns the same object, soDepends(...)anddependency_overrideskeep working);inline_classes=Truetreats every class as loop-safe;- everything else still goes through the threadpool, as do sync generator dependencies and sync endpoints.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi_cache_di import loop_safe, patch_fastapi_run_in_threadpool
patch_fastapi_run_in_threadpool(inline_classes=True)
@loop_safe
def get_tenant_id() -> str:
return "acme"
class UserRepository:
def __init__(self, tenant_id: Annotated[str, Depends(get_tenant_id)]) -> None:
self.tenant_id = tenant_id
app = FastAPI()
@app.get("/users")
async def list_users(repo: Annotated[UserRepository, Depends()]) -> str:
return repo.tenant_id
A loop-safe callable must never block: no I/O, no lock, no heavy computation. A blocking call on the event loop stalls every in-flight request. See the documentation for overrides and the marker shared with fastapi-standalone-di.
Skipping the sub-tree of cached dependencies
At request time, FastAPI solves the whole sub-tree of a dependency before
checking whether its value is already in the request's cache, so a dependency
shared across a deep tree is solved again at every occurrence.
patch_fastapi_solve_dependencies() stops that walk as soon as the value is
cached. It is opt-in and stays active for the app's lifetime:
from fastapi import FastAPI
from fastapi_cache_di import patch_fastapi_solve_dependencies
patch_fastapi_solve_dependencies()
app = FastAPI()
The values injected into endpoints are unchanged; see the documentation for the details.
Solving routes from a precompiled plan
Even with the sub-tree skip, FastAPI still walks the dependency tree
recursively at every request, and recomputes for each dependency what only
depends on the route: its cache key, the kind of its callable, the request
parameters it reads. patch_fastapi_resolution_plan() compiles each route's
dependency tree once, at the route's first request, into a flat plan in
FastAPI's evaluation order, then solves every request of that route from the
plan in a single loop. It is opt-in and stays active for the app's lifetime:
from fastapi import FastAPI
from fastapi_cache_di import patch_fastapi_resolution_plan
patch_fastapi_resolution_plan()
app = FastAPI()
On a route of 100 dependencies and 211 edges, solving a request takes about a
tenth of the time it takes with the sub-tree skip alone. The injected values,
the validation errors, the request's dependency cache and the exit order of
yield dependencies are the ones FastAPI produces, with dependency_overrides
too; see the documentation for the details.
Documentation
Full documentation is available at toilal.github.io/fastapi-cache-di (usage guide, internals, and API reference).
Requirements
- Python ≥ 3.12
- FastAPI ≥ 0.112.4
License
MIT © Rémi Alvergnat
CHANGELOG
v0.4.0 (2026-10-20)
Bug Fixes
Features
v0.3.0 (2026-10-16)
Bug Fixes
-
Patch get_flat_dependant only where FastAPI still defines it (#14,
30fbb28) -
Support FastAPI 0.141+ where get_flat_dependant no longer exists (#14,
30fbb28)
Features
-
Call loop-safe dependencies on the event loop instead of a threadpool hop (#16,
a37d214) -
Skip cached sub-trees when dependency_overrides is set too (#15,
bb8a2f7) -
Skip re-walking the sub-tree of already-cached dependencies at request time (#15,
bb8a2f7)
v0.2.1 (2026-07-06)
Bug Fixes
v0.2.0 (2026-07-05)
Features
v0.1.0 (2026-07-05)
- Initial Release
Changelog entries are generated automatically by python-semantic-release from Conventional Commits on release.
Metadata
Release files for fastapi-cache-di 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| fastapi_cache_di-0.4.0.tar.gz | 154.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_cache_di-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 177.8 kB
Release files / fastapi_cache_di-0.4.0.tar.gz
| Download URL | fastapi_cache_di-0.4.0.tar.gz |
|---|---|
| Size | 154.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6fd697012c5a66b284f3be06412911d6ed816898830f49015a7f83daae2e82a7
|
|
BLAKE2b-256 checksum How to use checksums |
bc20edc41f938772c38d955db2eaf3255369aa26a4077e9011cb3059309bb204
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / fastapi_cache_di-0.4.0-py3-none-any.whl
| Download URL | fastapi_cache_di-0.4.0-py3-none-any.whl |
|---|---|
| Size | 23.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fab64390df51286287bddd52a188b9405b00aa1e9245acec2aec90acfc7c96d5
|
|
BLAKE2b-256 checksum How to use checksums |
1077eeab0e175db08c295d39454cc7895e3c484b8ec1b13f6c74087596887bd4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.3 {"installer":{"name":"uv","version":"0.13.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for fastapi-cache-di 0.4.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.