Skip to main content

fastapi-depgraph

fastapi-depgraph logo

PyPI version Supported Python versions CI status License

Introspection of a FastAPI app's Depends() tree: which dependencies each route resolves, which are shared across routes, which aren't cached, and export to ASCII or Mermaid.

Reads route.dependant — the structure FastAPI already builds internally when each route is registered — instead of reparsing signatures by hand.

Install

pip install fastapi-depgraph

Usage — CLI

depgraph show myapp.main:app
depgraph show myapp.main:app --shared --uncached
depgraph export myapp.main:app --format mermaid > graph.mmd

Usage — API

from fastapi_depgraph import inspect_app

report = inspect_app(app)

report.shared_dependencies()    # {callable: ["/route1", "/route2"]}
report.uncached_dependencies()  # ["module.get_request_id", ...]

for route in report.routes:
    print(route.path, route.root.name)

Example

$ depgraph show examples/basic_app.py:app --shared --uncached
GET /users/me
  basic_app.read_current_user (sync)
  └── basic_app.get_current_user (sync)
      ├── basic_app.get_db (async)
      │   └── basic_app.get_settings (sync)
      └── basic_app.get_request_id (sync) [no-cache]
...

Dependencies shared across routes:
  get_current_user: /orders, /orders/{order_id}, /users/me
  get_db: /orders, /orders/{order_id}, /users, /users/me
  get_settings: /orders, /orders/{order_id}, /users, /users/me
  get_request_id: /orders, /orders/{order_id}, /users/me
  get_orders_service: /orders, /orders/{order_id}

Dependencies with use_cache=False:
  basic_app.get_request_id

At a glance: get_db and get_settings get resolved on four different routes — if one gets expensive, that's where to look first. And get_request_id is intentionally marked as uncached (it comes from a per-request header), but in a real case that's the flag that warns you about a use_cache=False someone forgot or added by mistake.

What patterns it handles

Tested, with regression coverage, against the real patterns that broke early versions of this package:

  • APIRouter + include_router(), at any level of nesting and with any prefix (/api → /v1 → /items correctly accumulates in the path).
  • Dependencies declared on include_router(router, dependencies=[...]) or FastAPI(dependencies=[...]) — an auth guard for an entire router shows up in the tree of every route it covers.
  • Apps mounted with app.mount(sub_app) (path caveat below).
  • Dependencies parametrized by a factory (def make_limiter(n): def check(): ...; return check) or functools.partial(fn, role="admin"): two instances with different captured/bound values show up as distinct nodes (check{n=5} vs. check{n=10}, require_role(role='admin') vs. require_role(role='editor')) instead of collapsing into one name.
  • Depends(SomeClass()) (instance) and Depends(SomeClass) (the class itself) — both "class as a dependency" patterns documented by FastAPI.

Known limitations

  • Apps mounted with app.mount(sub_app) are walked (Starlette exposes the sub-app's routes through Mount.routes), but the reported path is relative to the sub-app, without the mount's prefix.
  • Only HTTP routes are inspected; WebSocket routes aren't part of the tree (intentionally — Depends() on WebSocket doesn't have per-request caching or the same resolution model, see DESIGN.md §2).
  • If two different instances of the same class are used as dependencies on different routes, shared_dependencies() correctly treats them as not shared (it compares by identity), but they're shown with the same label in the tree/Mermaid unless the class itself is a closure — there's no generic way to give an arbitrary instance a readable name.
  • app.dependency_overrides (the standard mechanism for injecting test doubles) isn't reflected: the tree shows the dependency as declared in the code, not the one that would actually run under an active override — it's a static graph, see DESIGN.md §2.
  • The name of a parametrized dependency includes the repr() of its captured/bound values — if those values are secrets (tokens, API keys passed as defaults), they'll show up in depgraph show/export output. Don't paste that output into public channels without checking what parametrized dependencies your app has.
  • Merging the path/dependencies for included routers (include_router) uses, when available, an internal method with no public contract that Starlette exposes to resolve routes at request time; if that shape changes in a future version, the package silently degrades to the previous behavior (no accumulated prefix or inclusion dependencies) instead of failing — the CI matrix runs against several FastAPI versions to catch it early if that happens.

Why

FastAPI's DI system is implicit: nested Depends() resolve with no native way to see the tree. Two questions this package answers that today have no direct way to answer:

  • Which expensive dependencies are being recomputed instead of cached (use_cache=False)?
  • Which routes share an expensive dependency, so you know where to optimize once instead of in five different places?

See DESIGN.md for the rest of the scope and design decisions.

Metadata

Release files for fastapi-depgraph 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-depgraph 0.1.1
File Size Uploaded
fastapi_depgraph-0.1.1.tar.gz 812.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-depgraph 0.1.1
File Interpreter ABI Platform
fastapi_depgraph-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 823.9 kB

Release files / fastapi_depgraph-0.1.1.tar.gz

Download URL fastapi_depgraph-0.1.1.tar.gz
Size 812.8 kB
Tags Source
SHA-256 checksum
How to use checksums
33d28b03233ea81b2522c8278e84277195cd614416f9b51b53013c33f9dd5dde
BLAKE2b-256 checksum
How to use checksums
4542e6448dbaffe50d6967e8f24f670d85b0e8efbb9d0b48c048d41a84f04c64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.

Transparency log

Release files / fastapi_depgraph-0.1.1-py3-none-any.whl

Download URL fastapi_depgraph-0.1.1-py3-none-any.whl
Size 11.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8e33ac6148b937e88751cd3c47bf343971192a767510d55b916d080693808e75
BLAKE2b-256 checksum
How to use checksums
3a2562fd3b15fb2e45bbcebb6d7205ab2fc1116fe88b4418e354a4578d4e3e53
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 16, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page