fastapi-voyager 0.15.6__py3-none-any.whl → 0.16.0a2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. fastapi_voyager/__init__.py +2 -2
  2. fastapi_voyager/adapters/__init__.py +16 -0
  3. fastapi_voyager/adapters/base.py +44 -0
  4. fastapi_voyager/adapters/common.py +260 -0
  5. fastapi_voyager/adapters/django_ninja_adapter.py +299 -0
  6. fastapi_voyager/adapters/fastapi_adapter.py +167 -0
  7. fastapi_voyager/adapters/litestar_adapter.py +188 -0
  8. fastapi_voyager/cli.py +11 -5
  9. fastapi_voyager/er_diagram.py +15 -14
  10. fastapi_voyager/introspectors/__init__.py +34 -0
  11. fastapi_voyager/introspectors/base.py +81 -0
  12. fastapi_voyager/introspectors/detector.py +123 -0
  13. fastapi_voyager/introspectors/django_ninja.py +114 -0
  14. fastapi_voyager/introspectors/fastapi.py +94 -0
  15. fastapi_voyager/introspectors/litestar.py +166 -0
  16. fastapi_voyager/pydantic_resolve_util.py +4 -2
  17. fastapi_voyager/render.py +2 -2
  18. fastapi_voyager/render_style.py +0 -1
  19. fastapi_voyager/server.py +174 -295
  20. fastapi_voyager/type_helper.py +2 -2
  21. fastapi_voyager/version.py +1 -1
  22. fastapi_voyager/voyager.py +75 -47
  23. fastapi_voyager/web/index.html +11 -14
  24. fastapi_voyager/web/store.js +2 -0
  25. fastapi_voyager/web/vue-main.js +4 -0
  26. {fastapi_voyager-0.15.6.dist-info → fastapi_voyager-0.16.0a2.dist-info}/METADATA +152 -8
  27. {fastapi_voyager-0.15.6.dist-info → fastapi_voyager-0.16.0a2.dist-info}/RECORD +30 -18
  28. {fastapi_voyager-0.15.6.dist-info → fastapi_voyager-0.16.0a2.dist-info}/WHEEL +0 -0
  29. {fastapi_voyager-0.15.6.dist-info → fastapi_voyager-0.16.0a2.dist-info}/entry_points.txt +0 -0
  30. {fastapi_voyager-0.15.6.dist-info → fastapi_voyager-0.16.0a2.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,5 @@
1
1
 
2
2
  import pydantic_resolve.constant as const
3
- from fastapi import FastAPI, routing
4
3
  from pydantic import BaseModel
5
4
 
6
5
  from fastapi_voyager.filter import (
@@ -8,6 +7,7 @@ from fastapi_voyager.filter import (
8
7
  filter_subgraph_by_module_prefix,
9
8
  filter_subgraph_from_tag_to_schema_by_module_prefix,
10
9
  )
10
+ from fastapi_voyager.introspectors import AppIntrospector, RouteInfo
11
11
  from fastapi_voyager.render import Renderer
12
12
  from fastapi_voyager.type import PK, CoreData, FieldType, Link, LinkType, Route, SchemaNode, Tag
13
13
  from fastapi_voyager.type_helper import (
@@ -57,99 +57,127 @@ class Voyager:
57
57
  self.hide_primitive_route = hide_primitive_route
58
58
  self.show_module = show_module
59
59
  self.show_pydantic_resolve_meta = show_pydantic_resolve_meta
60
-
61
60
 
62
- def _get_available_route(self, app: FastAPI):
63
- for route in app.routes:
64
- if isinstance(route, routing.APIRoute):
65
- yield route
61
+ def _get_introspector(self, app) -> AppIntrospector:
62
+ """
63
+ Get the appropriate introspector for the given app.
64
+
65
+ Automatically detects the framework type and returns the matching introspector.
66
66
 
67
+ Args:
68
+ app: A web application instance or AppIntrospector
67
69
 
68
- def analysis(self, app: FastAPI):
70
+ Returns:
71
+ An AppIntrospector instance
72
+
73
+ Raises:
74
+ TypeError: If the app type is not supported
69
75
  """
76
+ from fastapi_voyager.introspectors import get_introspector
77
+
78
+ return get_introspector(app)
79
+
80
+ def analysis(self, app):
81
+ """
82
+ Analyze routes and schemas from a web application.
83
+
84
+ This method automatically detects the framework type and uses the appropriate
85
+ introspector. Supported frameworks:
86
+ - FastAPI (built-in)
87
+ - Any framework with a custom AppIntrospector implementation
88
+
89
+ Args:
90
+ app: A web application instance (FastAPI, Django Ninja API, etc.)
91
+ or an AppIntrospector instance for custom frameworks.
92
+
70
93
  1. get routes which return pydantic schema
71
94
  1.1 collect tags and routes, add links tag-> route
72
95
  1.2 collect response_model and links route -> response_model
73
96
 
74
97
  2. iterate schemas, construct the schema/model nodes and their links
75
98
  """
99
+ introspector = self._get_introspector(app)
76
100
  schemas: list[type[BaseModel]] = []
77
101
 
78
102
  # First, group all routes by tag
79
- routes_by_tag: dict[str, list] = {}
80
- for route in self._get_available_route(app):
81
- tags = getattr(route, 'tags', None)
82
-
103
+ routes_by_tag: dict[str, list[RouteInfo]] = {}
104
+ for route_info in introspector.get_routes():
83
105
  # using multiple tags is harmful, it's not recommended and will not be supported
84
- route_tag = tags[0] if tags else '__default__'
85
- routes_by_tag.setdefault(route_tag, []).append(route)
106
+ route_tag = route_info.tags[0] if route_info.tags else '__default__'
107
+ routes_by_tag.setdefault(route_tag, []).append(route_info)
86
108
 
87
109
  # Then filter by include_tags if provided
88
110
  if self.include_tags:
89
- filtered_routes_by_tag = {tag: routes for tag, routes in routes_by_tag.items()
90
- if tag in self.include_tags}
111
+ filtered_routes_by_tag = {
112
+ tag: routes
113
+ for tag, routes in routes_by_tag.items()
114
+ if tag in self.include_tags
115
+ }
91
116
  else:
92
117
  filtered_routes_by_tag = routes_by_tag
93
118
 
94
119
  # Process filtered routes
95
- for route_tag, routes in filtered_routes_by_tag.items():
96
-
120
+ for route_tag, route_infos in filtered_routes_by_tag.items():
97
121
  tag_id = f'tag__{route_tag}'
98
122
  tag_obj = Tag(id=tag_id, name=route_tag, routes=[])
99
123
  self.tags.append(tag_obj)
100
124
 
101
- for route in routes:
102
- # add route and create links
103
- route_id = full_class_name(route.endpoint)
104
- route_name = route.endpoint.__name__
105
- route_module = route.endpoint.__module__
106
-
125
+ for route_info in route_infos:
107
126
  # filter by route_name (route.id) if provided
108
- if self.route_name is not None and route_id != self.route_name:
127
+ if self.route_name is not None and route_info.id != self.route_name:
109
128
  continue
110
129
 
111
- is_primitive_response = is_non_pydantic_type(route.response_model)
130
+ is_primitive_response = is_non_pydantic_type(route_info.response_model)
112
131
  # filter primitive route if needed
113
132
  if self.hide_primitive_route and is_primitive_response:
114
133
  continue
115
134
 
116
- self.links.append(Link(
117
- source=tag_id,
118
- source_origin=tag_id,
119
- target=route_id,
120
- target_origin=route_id,
121
- type='tag_route'
122
- ))
135
+ self.links.append(
136
+ Link(
137
+ source=tag_id,
138
+ source_origin=tag_id,
139
+ target=route_info.id,
140
+ target_origin=route_info.id,
141
+ type='tag_route',
142
+ )
143
+ )
144
+
145
+ # Get unique_id from extra data if available
146
+ unique_id = route_info.operation_id
147
+ if route_info.extra and 'unique_id' in route_info.extra:
148
+ unique_id = unique_id or route_info.extra['unique_id']
123
149
 
124
150
  route_obj = Route(
125
- id=route_id,
126
- name=route_name,
127
- module=route_module,
128
- unique_id=route.operation_id or route.unique_id,
129
- response_schema=get_type_name(route.response_model),
130
- is_primitive=is_primitive_response
151
+ id=route_info.id,
152
+ name=route_info.name,
153
+ module=route_info.module,
154
+ unique_id=unique_id,
155
+ response_schema=get_type_name(route_info.response_model),
156
+ is_primitive=is_primitive_response,
131
157
  )
132
158
  self.routes.append(route_obj)
133
159
  tag_obj.routes.append(route_obj)
134
160
 
135
161
  # add response_models and create links from route -> response_model
136
- for schema in get_core_types(route.response_model):
162
+ for schema in get_core_types(route_info.response_model):
137
163
  if schema and issubclass(schema, BaseModel):
138
164
  is_primitive_response = False
139
165
  target_name = full_class_name(schema)
140
- self.links.append(Link(
141
- source=route_id,
142
- source_origin=route_id,
143
- target=self.generate_node_head(target_name),
144
- target_origin=target_name,
145
- type='route_to_schema'
146
- ))
166
+ self.links.append(
167
+ Link(
168
+ source=route_info.id,
169
+ source_origin=route_info.id,
170
+ target=self.generate_node_head(target_name),
171
+ target_origin=target_name,
172
+ type='route_to_schema',
173
+ )
174
+ )
147
175
 
148
176
  schemas.append(schema)
149
177
 
150
178
  for s in schemas:
151
179
  self.analysis_schemas(s)
152
-
180
+
153
181
  self.nodes = list(self.node_set.values())
154
182
 
155
183
 
@@ -4,32 +4,29 @@
4
4
  <meta name="theme-color" content="#ffffff" />
5
5
  <link
6
6
  rel="stylesheet"
7
- href="fastapi-voyager-static/graphviz.svg.css<!-- VERSION_PLACEHOLDER -->"
8
- />
9
- <link
10
- rel="stylesheet"
11
- href="fastapi-voyager-static/quasar.min.css<!-- VERSION_PLACEHOLDER -->"
7
+ href="<!-- STATIC_PATH -->/graphviz.svg.css<!-- VERSION_PLACEHOLDER -->"
12
8
  />
9
+ <link rel="stylesheet" href="<!-- STATIC_PATH -->/quasar.min.css<!-- VERSION_PLACEHOLDER -->" />
13
10
  <!-- App Icons / Favicons -->
14
11
  <link
15
12
  rel="apple-touch-icon"
16
13
  sizes="180x180"
17
- href="fastapi-voyager-static/icon/apple-touch-icon.png"
14
+ href="<!-- STATIC_PATH -->/icon/apple-touch-icon.png"
18
15
  />
19
16
  <link
20
17
  rel="icon"
21
18
  type="image/png"
22
19
  sizes="32x32"
23
- href="fastapi-voyager-static/icon/favicon-32x32.png"
20
+ href="<!-- STATIC_PATH -->/icon/favicon-32x32.png"
24
21
  />
25
22
  <link
26
23
  rel="icon"
27
24
  type="image/png"
28
25
  sizes="16x16"
29
- href="fastapi-voyager-static/icon/favicon-16x16.png"
26
+ href="<!-- STATIC_PATH -->/icon/favicon-16x16.png"
30
27
  />
31
- <link rel="icon" href="fastapi-voyager-static/icon/favicon.ico" sizes="any" />
32
- <link rel="manifest" href="fastapi-voyager-static/icon/site.webmanifest" />
28
+ <link rel="icon" href="<!-- STATIC_PATH -->/icon/favicon.ico" sizes="any" />
29
+ <link rel="manifest" href="<!-- STATIC_PATH -->/icon/site.webmanifest" />
33
30
  <link
34
31
  href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons"
35
32
  rel="stylesheet"
@@ -191,7 +188,7 @@
191
188
  style="font-size: 18px; font-weight: bold; display: flex; align-items: baseline"
192
189
  >
193
190
  <q-icon class="q-mr-sm" name="satellite_alt"></q-icon>
194
- <span> FastAPI Voyager </span>
191
+ <span> {{ store.state.framework_name }} Voyager </span>
195
192
  <span
196
193
  v-if="store.state.version"
197
194
  style="font-size: 12px; margin-left: 8px; font-weight: normal"
@@ -580,7 +577,7 @@
580
577
  </q-dialog>
581
578
  </div>
582
579
  <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
583
- <script src="fastapi-voyager-static/quasar.min.js<!-- VERSION_PLACEHOLDER -->"></script>
580
+ <script src="<!-- STATIC_PATH -->/quasar.min.js<!-- VERSION_PLACEHOLDER -->"></script>
584
581
  <script
585
582
  src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"
586
583
  integrity="sha512-egJ/Y+22P9NQ9aIyVCh0VCOsfydyn8eNmqBy+y2CnJG+fpRIxXMS6jbWP8tVKp0jp+NO5n8WtMUAnNnGoJKi4w=="
@@ -602,7 +599,7 @@
602
599
  ></script>
603
600
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.13/jquery.mousewheel.min.js"></script>
604
601
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-color/2.1.2/jquery.color.min.js"></script>
605
- <script src="fastapi-voyager-static/graphviz.svg.js<!-- VERSION_PLACEHOLDER -->"></script>
602
+ <script src="<!-- STATIC_PATH -->/graphviz.svg.js<!-- VERSION_PLACEHOLDER -->"></script>
606
603
  <!-- highlight.js minimal ES module load (python only) -->
607
604
  <link
608
605
  rel="stylesheet"
@@ -626,7 +623,7 @@
626
623
  </script>
627
624
  <script
628
625
  type="module"
629
- src="fastapi-voyager-static/vue-main.js<!-- VERSION_PLACEHOLDER -->"
626
+ src="<!-- STATIC_PATH -->/vue-main.js<!-- VERSION_PLACEHOLDER -->"
630
627
  ></script>
631
628
 
632
629
  <!-- GA_SNIPPET -->
@@ -2,6 +2,7 @@ const { reactive } = window.Vue
2
2
 
3
3
  const state = reactive({
4
4
  version: "",
5
+ framework_name: "",
5
6
  config: {
6
7
  initial_page_policy: "first",
7
8
  has_er_diagram: false,
@@ -305,6 +306,7 @@ const actions = {
305
306
  state.swagger.url = data.swagger_url || null
306
307
  state.config.has_er_diagram = data.has_er_diagram || false
307
308
  state.config.enable_pydantic_resolve_meta = data.enable_pydantic_resolve_meta || false
309
+ state.framework_name = data.framework_name || "API"
308
310
 
309
311
  this.rebuildSchemaOptions()
310
312
 
@@ -291,6 +291,10 @@ const app = createApp({
291
291
  onMounted(async () => {
292
292
  document.body.classList.remove("app-loading")
293
293
  await loadInitial()
294
+ // Update document title after framework_name is loaded
295
+ if (store.state.framework_name) {
296
+ document.title = `${store.state.framework_name} Voyager`
297
+ }
294
298
  // Reveal app content only after initial JS/data is ready
295
299
  })
296
300
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.15.6
3
+ Version: 0.16.0a2
4
4
  Summary: Visualize FastAPI application's routing tree and dependencies
5
5
  Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
6
6
  Project-URL: Source, https://github.com/allmonday/fastapi-voyager
@@ -18,13 +18,35 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Programming Language :: Python :: 3.14
20
20
  Requires-Python: >=3.10
21
- Requires-Dist: fastapi>=0.110
22
21
  Requires-Dist: jinja2>=3.0.0
23
22
  Requires-Dist: pydantic-resolve>=2.4.3
23
+ Provides-Extra: all
24
+ Requires-Dist: django-ninja>=1.5.3; extra == 'all'
25
+ Requires-Dist: django>=4.2; extra == 'all'
26
+ Requires-Dist: fastapi>=0.110; extra == 'all'
27
+ Requires-Dist: httpx; extra == 'all'
28
+ Requires-Dist: litestar>=2.19.0; extra == 'all'
29
+ Requires-Dist: pydantic>=2.0; extra == 'all'
30
+ Requires-Dist: pytest; extra == 'all'
31
+ Requires-Dist: pytest-asyncio; extra == 'all'
32
+ Requires-Dist: ruff; extra == 'all'
33
+ Requires-Dist: uvicorn; extra == 'all'
24
34
  Provides-Extra: dev
35
+ Requires-Dist: httpx; extra == 'dev'
25
36
  Requires-Dist: pytest; extra == 'dev'
37
+ Requires-Dist: pytest-asyncio; extra == 'dev'
26
38
  Requires-Dist: ruff; extra == 'dev'
27
- Requires-Dist: uvicorn; extra == 'dev'
39
+ Provides-Extra: django-ninja
40
+ Requires-Dist: django-ninja>=1.5.3; extra == 'django-ninja'
41
+ Requires-Dist: django>=4.2; extra == 'django-ninja'
42
+ Requires-Dist: uvicorn; extra == 'django-ninja'
43
+ Provides-Extra: fastapi
44
+ Requires-Dist: fastapi>=0.110; extra == 'fastapi'
45
+ Requires-Dist: uvicorn; extra == 'fastapi'
46
+ Provides-Extra: litestar
47
+ Requires-Dist: litestar>=2.19.0; extra == 'litestar'
48
+ Requires-Dist: pydantic>=2.0; extra == 'litestar'
49
+ Requires-Dist: uvicorn; extra == 'litestar'
28
50
  Description-Content-Type: text/markdown
29
51
 
30
52
  [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
@@ -34,10 +56,12 @@ Description-Content-Type: text/markdown
34
56
 
35
57
  # FastAPI Voyager
36
58
 
37
- Visualize your FastAPI endpoints and explore them interactively.
59
+ Visualize your API endpoints and explore them interactively.
38
60
 
39
61
  Its vision is to make code easier to read and understand, serving as an ideal documentation tool.
40
62
 
63
+ **Now supports multiple frameworks:** FastAPI, Django Ninja, and Litestar.
64
+
41
65
  > This repo is still in early stage, it supports Pydantic v2 only.
42
66
 
43
67
  - **Live Demo**: https://www.newsyeah.fun/voyager/
@@ -49,6 +73,7 @@ Its vision is to make code easier to read and understand, serving as an ideal do
49
73
 
50
74
  - [Quick Start](#quick-start)
51
75
  - [Installation](#installation)
76
+ - [Supported Frameworks](#supported-frameworks)
52
77
  - [Features](#features)
53
78
  - [Command Line Usage](#command-line-usage)
54
79
  - [About pydantic-resolve](#about-pydantic-resolve)
@@ -58,7 +83,7 @@ Its vision is to make code easier to read and understand, serving as an ideal do
58
83
 
59
84
  ## Quick Start
60
85
 
61
- With simple configuration, fastapi-voyager can be embedded into FastAPI:
86
+ With simple configuration, fastapi-voyager can be embedded into your web application:
62
87
 
63
88
  ```python
64
89
  from fastapi import FastAPI
@@ -66,6 +91,8 @@ from fastapi_voyager import create_voyager
66
91
 
67
92
  app = FastAPI()
68
93
 
94
+ # ... define your routes ...
95
+
69
96
  app.mount('/voyager',
70
97
  create_voyager(
71
98
  app,
@@ -74,12 +101,14 @@ app.mount('/voyager',
74
101
  swagger_url="/docs",
75
102
  ga_id="G-XXXXXXXXVL",
76
103
  initial_page_policy='first',
77
- online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master',
104
+ online_repo_url='https://github.com/your-org/your-repo/blob/master',
78
105
  enable_pydantic_resolve_meta=True))
79
106
  ```
80
107
 
81
108
  Visit `http://localhost:8000/voyager` to explore your API visually.
82
109
 
110
+ For framework-specific examples (Django Ninja, Litestar), see [Supported Frameworks](#supported-frameworks).
111
+
83
112
  [View full example](https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48)
84
113
 
85
114
  ## Installation
@@ -110,9 +139,106 @@ voyager -m path.to.your.app.module --server --app api
110
139
 
111
140
  > **Note**: [Sub-Application mounts](https://fastapi.tiangolo.com/advanced/sub-applications/) are not supported yet, but you can specify the name of the FastAPI application with `--app`. Only a single application (default: `app`) can be selected.
112
141
 
142
+ ## Supported Frameworks
143
+
144
+ fastapi-voyager automatically detects your framework and provides the appropriate integration. Currently supported frameworks:
145
+
146
+ ### FastAPI
147
+
148
+ ```python
149
+ from fastapi import FastAPI
150
+ from fastapi_voyager import create_voyager
151
+
152
+ app = FastAPI()
153
+
154
+ @app.get("/hello")
155
+ def hello():
156
+ return {"message": "Hello World"}
157
+
158
+ # Mount voyager
159
+ app.mount("/voyager", create_voyager(app))
160
+ ```
161
+
162
+ Start with:
163
+ ```bash
164
+ uvicorn your_app:app --reload
165
+ # Visit http://localhost:8000/voyager
166
+ ```
167
+
168
+ ### Django Ninja
169
+
170
+ ```python
171
+ import os
172
+ import django
173
+ from django.core.asgi import get_asgi_application
174
+ from ninja import NinjaAPI
175
+ from fastapi_voyager import create_voyager
176
+
177
+ # Configure Django
178
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings")
179
+ django.setup()
180
+
181
+ # Create Django Ninja API
182
+ api = NinjaAPI()
183
+
184
+ @api.get("/hello")
185
+ def hello(request):
186
+ return {"message": "Hello World"}
187
+
188
+ # Create voyager ASGI app
189
+ voyager_app = create_voyager(api)
190
+
191
+ # Create ASGI application that routes between Django and voyager
192
+ async def application(scope, receive, send):
193
+ if scope["type"] == "http" and scope["path"].startswith("/voyager"):
194
+ await voyager_app(scope, receive, send)
195
+ else:
196
+ django_app = get_asgi_application()
197
+ await django_app(scope, receive, send)
198
+ ```
199
+
200
+ Start with:
201
+ ```bash
202
+ uvicorn your_app:application --reload
203
+ # Visit http://localhost:8000/voyager
204
+ ```
205
+
206
+ ### Litestar
207
+
208
+ ```python
209
+ from litestar import Litestar, get
210
+ from fastapi_voyager import create_voyager
211
+
212
+ # Create Litestar app
213
+ app = Litestar()
214
+
215
+ @get("/hello")
216
+ def hello() -> dict:
217
+ return {"message": "Hello World"}
218
+
219
+ # Create voyager app (returns a Litestar app)
220
+ voyager_app = create_voyager(app)
221
+
222
+ # Create ASGI application that routes between main app and voyager
223
+ async def asgi_app(scope, receive, send):
224
+ if scope["type"] == "http" and scope["path"].startswith("/voyager"):
225
+ # Remove /voyager prefix for voyager app
226
+ new_scope = dict(scope)
227
+ new_scope["path"] = scope["path"][8:] or "/"
228
+ await voyager_app(new_scope, receive, send)
229
+ else:
230
+ await app(scope, receive, send)
231
+ ```
232
+
233
+ Start with:
234
+ ```bash
235
+ uvicorn your_app:asgi_app --reload
236
+ # Visit http://localhost:8000/voyager
237
+ ```
238
+
113
239
  ## Features
114
240
 
115
- fastapi-voyager is designed for scenarios using FastAPI as internal API integration endpoints. It helps visualize dependencies and serves as an architecture tool to identify implementation issues such as wrong relationships, overfetching, and more.
241
+ fastapi-voyager is designed for scenarios using web frameworks with Pydantic models (FastAPI, Django Ninja, Litestar). It helps visualize dependencies and serves as an architecture tool to identify implementation issues such as wrong relationships, overfetching, and more.
116
242
 
117
243
  **Best Practice**: When building view models following the ER model pattern, fastapi-voyager can fully realize its potential - quickly identifying which APIs use specific entities and vice versa.
118
244
 
@@ -258,6 +384,21 @@ uv pip install ".[dev]"
258
384
  uvicorn tests.programatic:app --reload
259
385
  ```
260
386
 
387
+ ### Test Different Frameworks
388
+
389
+ You can test the framework-specific examples:
390
+
391
+ ```bash
392
+ # FastAPI example
393
+ uvicorn tests.fastapi.embedding:app --reload
394
+
395
+ # Django Ninja example
396
+ uvicorn tests.django_ninja.embedding:app --reload
397
+
398
+ # Litestar example
399
+ uvicorn tests.litestar.embedding:asgi_app --reload
400
+ ```
401
+
261
402
  Visit `http://localhost:8000/voyager` to see changes.
262
403
 
263
404
  ### Setup Git Hooks (Optional)
@@ -289,9 +430,12 @@ This will run Prettier automatically before each commit. See [`.githooks/README.
289
430
 
290
431
  ## Dependencies
291
432
 
292
- - [FastAPI](https://fastapi.tiangolo.com/)
293
433
  - [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
294
434
  - [Quasar Framework](https://quasar.dev/)
435
+ ### Dev dependencies
436
+ - [FastAPI](https://fastapi.tiangolo.com/)
437
+ - [Django Ninja](https://django-ninja.rest-framework.com/)
438
+ - [Litestar](https://litestar.dev/)
295
439
 
296
440
  ## Credits
297
441
 
@@ -1,16 +1,28 @@
1
- fastapi_voyager/__init__.py,sha256=kqwzThE1YhmQ_7jPKGlnWvqRGC-hFrRqq_lKhKaleYU,229
2
- fastapi_voyager/cli.py,sha256=td3yIIigEomhSdDO-Xkh-CgpEwCafwlwnpvxnT9QsBo,10488
3
- fastapi_voyager/er_diagram.py,sha256=AbaKbcNd3cgCaQBsE0aHzvljTiZza3bmRiGJI3BGhGo,7873
1
+ fastapi_voyager/__init__.py,sha256=1IdDy6JUMgQqQo33qUe2znX9YeI7S35pjVrbt0QLOzY,228
2
+ fastapi_voyager/cli.py,sha256=1o0areaN302fOoBprn-zXPnDFfCFbF81MatFTR1FHrU,10795
3
+ fastapi_voyager/er_diagram.py,sha256=4Ba5u-T7XmVmk9MltVMe-m-18mUAHMTybTdE-zNZLrU,7860
4
4
  fastapi_voyager/filter.py,sha256=AN_HIu8-DtKisIq5mFt7CnqRHtxKewedNGyyaI82hSY,11529
5
5
  fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,3425
6
- fastapi_voyager/pydantic_resolve_util.py,sha256=r4Rq7BtBcFOMV7O2Ab9TwLyRNL1yNDiQlGUVybf-sXs,3524
7
- fastapi_voyager/render.py,sha256=5tTuvvCCUwFCq3WJGT1rfTSW41mSDoVyJwMyQGrmhiQ,17271
8
- fastapi_voyager/render_style.py,sha256=mPOuChEl71-3agCbPwkMt2sFmax2AEKDI6dK90eFPRc,2552
9
- fastapi_voyager/server.py,sha256=E0gGU7D1pBvnV7gFLBUvnkwtiBFbU1PbxEXHHSIA1oA,9115
6
+ fastapi_voyager/pydantic_resolve_util.py,sha256=0UfAp6Yi6FNpsI1bUu89hRVWFy6keBu1KtcZl-6NYso,3526
7
+ fastapi_voyager/render.py,sha256=A1jFDraQFOfnFHguYlsvBbGIDJ527VQH0jZ-xgTjqIk,17270
8
+ fastapi_voyager/render_style.py,sha256=1y3aRhBSJSWU-JuSgjn9il_xFEqjv6mJCoUzImLQT6M,2525
9
+ fastapi_voyager/server.py,sha256=sgUUscbt736VNB6CN2J_6rJ6fpWO4vJFW7oP7FmAjvA,6739
10
10
  fastapi_voyager/type.py,sha256=zluWvh5vpnjXJ9aAmyNJTSmXZPjAHCvgRT5oQRAjHrg,2104
11
- fastapi_voyager/type_helper.py,sha256=FmfrZAI3Z4uDdh3sH_kH7UGoY6yNVPapneSN86qY_wo,10209
12
- fastapi_voyager/version.py,sha256=ILB2INUiq4EGupYfMT2L0gsR_3dHm9fPDuoUnU5BSf0,49
13
- fastapi_voyager/voyager.py,sha256=4vonmL-xt54C5San-DRBq4mjoV8Q96eoWRy68MJ1IJw,14169
11
+ fastapi_voyager/type_helper.py,sha256=5HYUHdghTISZ44NvVsrMWlRGD5C9-xrGeNKuLYDMA6s,10209
12
+ fastapi_voyager/version.py,sha256=BzQ3H9QQr3tEif7581G5jfiOo26aX4wMpelAXUnXzF0,56
13
+ fastapi_voyager/voyager.py,sha256=S0cCMLFv_wPfEMVdp4cYqbc-Y207SjCTSmsYdqzIDHg,15307
14
+ fastapi_voyager/adapters/__init__.py,sha256=a95rBvV4QVcX_yAzjuJQKOW-EbJ79R--YjUJ3BGkC3k,517
15
+ fastapi_voyager/adapters/base.py,sha256=m-E74LlNgYCpj-gqfYsKl2mzWW5iFiaDTjiBri_5cfo,1283
16
+ fastapi_voyager/adapters/common.py,sha256=DXVLsjLn65U3RR7YyKL_ELV74mnINWmZ4hQ6zISdV0E,9684
17
+ fastapi_voyager/adapters/django_ninja_adapter.py,sha256=tl1rMcotAhOwBxT8poG8rTXm1v3wn_cpaoda79NEFxE,11424
18
+ fastapi_voyager/adapters/fastapi_adapter.py,sha256=attD4uuKXElFXk7JT7W-JW5B4S7a-qHzYVToHKBwTqk,5984
19
+ fastapi_voyager/adapters/litestar_adapter.py,sha256=9AfWuLtHHqzKj-VySJaOEtb7HWefRfJSobCQ_Y03ytU,6908
20
+ fastapi_voyager/introspectors/__init__.py,sha256=HbmoUyM-BSwd4Rg2ptd9u-qvZSD3UykKyHYVoRg03OM,917
21
+ fastapi_voyager/introspectors/base.py,sha256=hMfka9gVXr-E8MA1rKSSmYk0OppqgiFPWavfgAkPmQI,2131
22
+ fastapi_voyager/introspectors/detector.py,sha256=rmlpQARJMrFXPty6OUdjmFRTtBYErGDH4l7Tivnu_FQ,3910
23
+ fastapi_voyager/introspectors/django_ninja.py,sha256=Ytneh_kpsakTo_Njv1e4nvqfErjT1PrYelLZ-8xX5Gg,4052
24
+ fastapi_voyager/introspectors/fastapi.py,sha256=TGr1VPO0f6y-w3ZvQpI15o4Jq5L7VcN_twqzY_33fz4,3085
25
+ fastapi_voyager/introspectors/litestar.py,sha256=QXnaT0-hCa_0sByKJoUWuu0vIzRpCCKLokCBDTtv_s4,6100
14
26
  fastapi_voyager/templates/dot/cluster.j2,sha256=I2z9KkfCzmAtqXe0gXBnxnOfBXUSpdlATs3uf-O8_B8,307
15
27
  fastapi_voyager/templates/dot/cluster_container.j2,sha256=2tH1mOJvPoVKE_aHVMR3t06TfH_dYa9OeH6DBqSHt_A,204
16
28
  fastapi_voyager/templates/dot/digraph.j2,sha256=wZuiO-vvZ-AJ1FcMQG4BLevUyxk6yA-yEpUa3Us05mE,435
@@ -27,11 +39,11 @@ fastapi_voyager/templates/html/schema_table.j2,sha256=rzphiGk1il7uv4Gr2p_HLPHqyL
27
39
  fastapi_voyager/web/graph-ui.js,sha256=9b2auyGWEpUcF65YV231GNojQ9Uk6FsT1SlRR3lSYnc,7664
28
40
  fastapi_voyager/web/graphviz.svg.css,sha256=K218ov_mdSe3ga4KwhiBB92ynVvm5zaAk9_D9a3d8hE,1546
29
41
  fastapi_voyager/web/graphviz.svg.js,sha256=VokgCghvP4zm3SFiFVPIikdW6XzjkZJXQkBbCrEitug,19885
30
- fastapi_voyager/web/index.html,sha256=wM9vJ_UfHR8p98F6SEMCKKjJcBEl0EyosWuPqVZYXvA,23496
42
+ fastapi_voyager/web/index.html,sha256=4HGVavdd15u_BA7BwC-RJEeKal6hcYbiQaLcoOANQHI,23485
31
43
  fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
32
44
  fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
33
- fastapi_voyager/web/store.js,sha256=vwiqeLw7DfTOUDu9oN7b6nSBw8KG6W-5Dc-voQ1leL8,15113
34
- fastapi_voyager/web/vue-main.js,sha256=4lJi6ADrcaOzjXcQePkp7CyiCkAnvhnO-nkF3E6G3s4,10650
45
+ fastapi_voyager/web/store.js,sha256=CKQz5tZtv6W70TDVqeQiOt2gUXzD0WIc0ufCa0dB8sU,15193
46
+ fastapi_voyager/web/vue-main.js,sha256=MedNrfgy2KJCFiyv8sczC8d8M2yytZDX58PQGvjUYdM,10825
35
47
  fastapi_voyager/web/component/demo.js,sha256=sAklFGhKGmMy9-ofgOw2oPIidAoIOgHu6yvV51L_MAA,350
36
48
  fastapi_voyager/web/component/render-graph.js,sha256=9wnO70n3eyPKTpa744idgs5PSwgvzbfv4InZ68eEOKs,2454
37
49
  fastapi_voyager/web/component/route-code-display.js,sha256=a823nBz3EEjutW2pfi73rcF3hodCBmgYNmuZi94sXE4,3615
@@ -43,8 +55,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
43
55
  fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
44
56
  fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
45
57
  fastapi_voyager/web/icon/site.webmanifest,sha256=GRozZ5suTykYcPMap1QhjrAB8PLW0mbT_phhzw_utvQ,316
46
- fastapi_voyager-0.15.6.dist-info/METADATA,sha256=D-09okYHB5uaatoDc5SqmOVr_jTbw18o4_76pmxO4_g,9870
47
- fastapi_voyager-0.15.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
48
- fastapi_voyager-0.15.6.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
49
- fastapi_voyager-0.15.6.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
50
- fastapi_voyager-0.15.6.dist-info/RECORD,,
58
+ fastapi_voyager-0.16.0a2.dist-info/METADATA,sha256=SskYV4bCVQW96Q5CYbgZQEVt_ajQWPG1BO0uzKnxKmA,13701
59
+ fastapi_voyager-0.16.0a2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
60
+ fastapi_voyager-0.16.0a2.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
61
+ fastapi_voyager-0.16.0a2.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
62
+ fastapi_voyager-0.16.0a2.dist-info/RECORD,,