fastapi-voyager 0.15.3__tar.gz → 0.15.5__tar.gz

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 (85) hide show
  1. fastapi_voyager-0.15.5/PKG-INFO +303 -0
  2. fastapi_voyager-0.15.5/README.md +274 -0
  3. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/docs/changelog.md +12 -1
  4. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/server.py +4 -1
  5. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/version.py +1 -1
  6. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/graph-ui.js +38 -3
  7. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/index.html +21 -9
  8. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/store.js +27 -6
  9. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/vue-main.js +2 -0
  10. fastapi_voyager-0.15.3/PKG-INFO +0 -256
  11. fastapi_voyager-0.15.3/README.md +0 -227
  12. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.githooks/README.md +0 -0
  13. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.githooks/pre-commit +0 -0
  14. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  15. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  16. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.github/workflows/publish.yml +0 -0
  17. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.gitignore +0 -0
  18. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.prettierignore +0 -0
  19. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.prettierrc +0 -0
  20. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/.python-version +0 -0
  21. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/CONTRIBUTING.md +0 -0
  22. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/LICENSE +0 -0
  23. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/docs/claude/0_REFACTORING_RENDER_NOTES.md +0 -0
  24. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/docs/idea.md +0 -0
  25. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/package-lock.json +0 -0
  26. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/pyproject.toml +0 -0
  27. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/release.md +0 -0
  28. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/setup-hooks.sh +0 -0
  29. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/__init__.py +0 -0
  30. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/cli.py +0 -0
  31. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/er_diagram.py +0 -0
  32. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/filter.py +0 -0
  33. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/module.py +0 -0
  34. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/pydantic_resolve_util.py +0 -0
  35. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/render.py +0 -0
  36. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/render_style.py +0 -0
  37. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/cluster.j2 +0 -0
  38. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/cluster_container.j2 +0 -0
  39. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/digraph.j2 +0 -0
  40. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/er_diagram.j2 +0 -0
  41. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/link.j2 +0 -0
  42. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/route_node.j2 +0 -0
  43. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/schema_node.j2 +0 -0
  44. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/dot/tag_node.j2 +0 -0
  45. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/html/colored_text.j2 +0 -0
  46. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/html/pydantic_meta.j2 +0 -0
  47. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/html/schema_field_row.j2 +0 -0
  48. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/html/schema_header.j2 +0 -0
  49. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/templates/html/schema_table.j2 +0 -0
  50. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/type.py +0 -0
  51. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/type_helper.py +0 -0
  52. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/voyager.py +0 -0
  53. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/component/demo.js +0 -0
  54. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/component/render-graph.js +0 -0
  55. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
  56. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/component/schema-code-display.js +0 -0
  57. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
  58. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/graphviz.svg.js +0 -0
  59. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
  60. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
  61. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
  62. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
  63. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
  64. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/icon/favicon.ico +0 -0
  65. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/icon/site.webmanifest +0 -0
  66. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/quasar.min.css +0 -0
  67. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/src/fastapi_voyager/web/quasar.min.js +0 -0
  68. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/__init__.py +0 -0
  69. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/demo.py +0 -0
  70. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/demo_anno.py +0 -0
  71. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/programatic.py +0 -0
  72. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/service/__init__.py +0 -0
  73. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/service/schema/__init__.py +0 -0
  74. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/service/schema/base_entity.py +0 -0
  75. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/service/schema/extra.py +0 -0
  76. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/service/schema/schema.py +0 -0
  77. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/test_analysis.py +0 -0
  78. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/test_filter.py +0 -0
  79. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/test_generic.py +0 -0
  80. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/test_import.py +0 -0
  81. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/test_module.py +0 -0
  82. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/test_resolve_util_impl.py +0 -0
  83. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/tests/test_type_helper.py +0 -0
  84. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/uv.lock +0 -0
  85. {fastapi_voyager-0.15.3 → fastapi_voyager-0.15.5}/voyager.jpg +0 -0
@@ -0,0 +1,303 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-voyager
3
+ Version: 0.15.5
4
+ Summary: Visualize FastAPI application's routing tree and dependencies
5
+ Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
6
+ Project-URL: Source, https://github.com/allmonday/fastapi-voyager
7
+ Author-email: Tangkikodo <allmonday@126.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: fastapi,openapi,routing,visualization
11
+ Classifier: Framework :: FastAPI
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: fastapi>=0.110
22
+ Requires-Dist: jinja2>=3.0.0
23
+ Requires-Dist: pydantic-resolve>=2.4.3
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest; extra == 'dev'
26
+ Requires-Dist: ruff; extra == 'dev'
27
+ Requires-Dist: uvicorn; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
31
+ ![Python Versions](https://img.shields.io/pypi/pyversions/fastapi-voyager)
32
+ [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-voyager/month)](https://pepy.tech/projects/fastapi-voyager)
33
+
34
+
35
+ # FastAPI Voyager
36
+
37
+ Visualize your FastAPI endpoints and explore them interactively.
38
+
39
+ Its vision is to make code easier to read and understand, serving as an ideal documentation tool.
40
+
41
+ > This repo is still in early stage, it supports Pydantic v2 only.
42
+
43
+ - **Live Demo**: https://www.newsyeah.fun/voyager/
44
+ - **Example Source**: [composition-oriented-development-pattern](https://github.com/allmonday/composition-oriented-development-pattern)
45
+
46
+ <img width="1597" height="933" alt="fastapi-voyager overview" src="https://github.com/user-attachments/assets/020bf5b2-6c69-44bf-ba1f-39389d388d27" />
47
+
48
+ ## Table of Contents
49
+
50
+ - [Quick Start](#quick-start)
51
+ - [Installation](#installation)
52
+ - [Features](#features)
53
+ - [Command Line Usage](#command-line-usage)
54
+ - [About pydantic-resolve](#about-pydantic-resolve)
55
+ - [Development](#development)
56
+ - [Dependencies](#dependencies)
57
+ - [Credits](#credits)
58
+
59
+ ## Quick Start
60
+
61
+ With simple configuration, fastapi-voyager can be embedded into FastAPI:
62
+
63
+ ```python
64
+ from fastapi import FastAPI
65
+ from fastapi_voyager import create_voyager
66
+
67
+ app = FastAPI()
68
+
69
+ app.mount('/voyager',
70
+ create_voyager(
71
+ app,
72
+ module_color={'src.services': 'tomato'},
73
+ module_prefix='src.services',
74
+ swagger_url="/docs",
75
+ ga_id="G-XXXXXXXXVL",
76
+ initial_page_policy='first',
77
+ online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master',
78
+ enable_pydantic_resolve_meta=True))
79
+ ```
80
+
81
+ Visit `http://localhost:8000/voyager` to explore your API visually.
82
+
83
+ [View full example](https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48)
84
+
85
+ ## Installation
86
+
87
+ ### Install via pip
88
+
89
+ ```bash
90
+ pip install fastapi-voyager
91
+ ```
92
+
93
+ ### Install via uv
94
+
95
+ ```bash
96
+ uv add fastapi-voyager
97
+ ```
98
+
99
+ ### Run with CLI
100
+
101
+ ```bash
102
+ voyager -m path.to.your.app.module --server
103
+ ```
104
+
105
+ For sub-application scenarios (e.g., `app.mount("/api", api)`), specify the app name:
106
+
107
+ ```bash
108
+ voyager -m path.to.your.app.module --server --app api
109
+ ```
110
+
111
+ > **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
+
113
+ ## Features
114
+
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.
116
+
117
+ **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
+
119
+ ### Highlight Nodes and Links
120
+
121
+ Click a node to highlight its upstream and downstream nodes. Figure out the related models of one page, or how many pages are related with one model.
122
+
123
+ <img width="1100" height="700" alt="highlight nodes and dependencies" src="https://github.com/user-attachments/assets/3e0369ea-5fa4-469a-82c1-ed57d407e53d" />
124
+
125
+ ### View Source Code
126
+
127
+ Double-click a node or route to show source code or open the file in VSCode.
128
+
129
+ <img width="1297" height="940" alt="view source code" src="https://github.com/user-attachments/assets/c8bb2e7d-b727-42a6-8c9e-64dce297d2d8" />
130
+
131
+ ### Quick Search
132
+
133
+ Search schemas by name and display their upstream and downstream dependencies. Use `Shift + Click` on any node to quickly search for it.
134
+
135
+ <img width="1587" height="873" alt="quick search functionality" src="https://github.com/user-attachments/assets/ee4716f3-233d-418f-bc0e-3b214d1498f7" />
136
+
137
+ ### Display ER Diagram
138
+
139
+ ER diagram is a feature from pydantic-resolve which provides a solid expression for business descriptions. You can visualize application-level entity relationship diagrams.
140
+
141
+ ```python
142
+ from pydantic_resolve import ErDiagram, Entity, Relationship
143
+
144
+ diagram = ErDiagram(
145
+ configs=[
146
+ Entity(
147
+ kls=Team,
148
+ relationships=[
149
+ Relationship(field='id', target_kls=list[Sprint], loader=sprint_loader.team_to_sprint_loader),
150
+ Relationship(field='id', target_kls=list[User], loader=user_loader.team_to_user_loader)
151
+ ]
152
+ ),
153
+ Entity(
154
+ kls=Sprint,
155
+ relationships=[
156
+ Relationship(field='id', target_kls=list[Story], loader=story_loader.sprint_to_story_loader)
157
+ ]
158
+ ),
159
+ Entity(
160
+ kls=Story,
161
+ relationships=[
162
+ Relationship(field='id', target_kls=list[Task], loader=task_loader.story_to_task_loader),
163
+ Relationship(field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
164
+ ]
165
+ ),
166
+ Entity(
167
+ kls=Task,
168
+ relationships=[
169
+ Relationship(field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
170
+ ]
171
+ )
172
+ ]
173
+ )
174
+
175
+ # Display in voyager
176
+ app.mount('/voyager',
177
+ create_voyager(
178
+ app,
179
+ er_diagram=diagram
180
+ ))
181
+ ```
182
+
183
+ <img width="1276" height="613" alt="ER diagram visualization" src="https://github.com/user-attachments/assets/ea0091bb-ee11-4f71-8be3-7129d956c910" />
184
+
185
+ ### Show Pydantic Resolve Meta Info
186
+
187
+ Set `enable_pydantic_resolve_meta=True` in `create_voyager`, then toggle the "pydantic resolve meta" button to visualize resolve/post/expose/collect operations.
188
+
189
+ <img width="1604" height="535" alt="pydantic resolve meta information" src="https://github.com/user-attachments/assets/d1639555-af41-4a08-9970-4b8ef314596a" />
190
+
191
+ ## Command Line Usage
192
+
193
+ ### Start Server
194
+
195
+ ```bash
196
+ # Open in browser (default port 8000)
197
+ voyager -m tests.demo --server
198
+
199
+ # Custom port
200
+ voyager -m tests.demo --server --port=8002
201
+
202
+ # Specify app name
203
+ voyager -m tests.demo --server --app my_app
204
+ ```
205
+
206
+ ### Generate DOT File
207
+
208
+ ```bash
209
+ # Generate .dot file
210
+ voyager -m tests.demo
211
+
212
+ # Specify app
213
+ voyager -m tests.demo --app my_app
214
+
215
+ # Filter by schema
216
+ voyager -m tests.demo --schema Task
217
+
218
+ # Show all fields
219
+ voyager -m tests.demo --show_fields all
220
+
221
+ # Custom module colors
222
+ voyager -m tests.demo --module_color=tests.demo:red --module_color=tests.service:tomato
223
+
224
+ # Output to file
225
+ voyager -m tests.demo -o my_visualization.dot
226
+
227
+ # Version and help
228
+ voyager --version
229
+ voyager --help
230
+ ```
231
+
232
+ ## About pydantic-resolve
233
+
234
+ pydantic-resolve is a lightweight tool designed to build complex, nested data in a simple, declarative way. In v2, it introduced an important feature: **ER Diagram**, and fastapi-voyager has supported this feature, allowing for a clearer understanding of business relationships.
235
+
236
+ The ~~`@ensure_subset` decorator~~ `DefineSubset` metaclass helps safely pick fields from the 'source class' while **indicating the reference** from the current class to the base class.
237
+
238
+ Developers can use fastapi-voyager without needing to know anything about pydantic-resolve, but I still highly recommend everyone to give it a try.
239
+
240
+ ## Development
241
+
242
+ ### Setup Development Environment
243
+
244
+ ```bash
245
+ # Fork and clone the repository
246
+ git clone https://github.com/your-username/fastapi-voyager.git
247
+ cd fastapi-voyager
248
+
249
+ # Install uv
250
+ curl -LsSf https://astral.sh/uv/install.sh | sh
251
+
252
+ # Create virtual environment and install dependencies
253
+ uv venv
254
+ source .venv/bin/activate
255
+ uv pip install ".[dev]"
256
+
257
+ # Run development server
258
+ uvicorn tests.programatic:app --reload
259
+ ```
260
+
261
+ Visit `http://localhost:8000/voyager` to see changes.
262
+
263
+ ### Setup Git Hooks (Optional)
264
+
265
+ Enable automatic code formatting before commits:
266
+
267
+ ```bash
268
+ ./setup-hooks.sh
269
+ # or manually:
270
+ git config core.hooksPath .githooks
271
+ ```
272
+
273
+ This will run Prettier automatically before each commit. See [`.githooks/README.md`](./.githooks/README.md) for details.
274
+
275
+ ### Project Structure
276
+
277
+ **Frontend:**
278
+ - `src/fastapi_voyager/web/vue-main.js` - Main JavaScript entry
279
+
280
+ **Backend:**
281
+ - `voyager.py` - Main entry point
282
+ - `render.py` - Generate DOT files
283
+ - `server.py` - Server mode
284
+
285
+ ## Roadmap
286
+
287
+ - [Ideas](./docs/idea.md)
288
+ - [Changelog & Roadmap](./docs/changelog.md)
289
+
290
+ ## Dependencies
291
+
292
+ - [FastAPI](https://fastapi.tiangolo.com/)
293
+ - [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
294
+ - [Quasar Framework](https://quasar.dev/)
295
+
296
+ ## Credits
297
+
298
+ - [graphql-voyager](https://apis.guru/graphql-voyager/) - Thanks for inspiration
299
+ - [vscode-interactive-graphviz](https://github.com/tintinweb/vscode-interactive-graphviz) - Thanks for web visualization
300
+
301
+ ## License
302
+
303
+ MIT License
@@ -0,0 +1,274 @@
1
+ [![pypi](https://img.shields.io/pypi/v/fastapi-voyager.svg)](https://pypi.python.org/pypi/fastapi-voyager)
2
+ ![Python Versions](https://img.shields.io/pypi/pyversions/fastapi-voyager)
3
+ [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-voyager/month)](https://pepy.tech/projects/fastapi-voyager)
4
+
5
+
6
+ # FastAPI Voyager
7
+
8
+ Visualize your FastAPI endpoints and explore them interactively.
9
+
10
+ Its vision is to make code easier to read and understand, serving as an ideal documentation tool.
11
+
12
+ > This repo is still in early stage, it supports Pydantic v2 only.
13
+
14
+ - **Live Demo**: https://www.newsyeah.fun/voyager/
15
+ - **Example Source**: [composition-oriented-development-pattern](https://github.com/allmonday/composition-oriented-development-pattern)
16
+
17
+ <img width="1597" height="933" alt="fastapi-voyager overview" src="https://github.com/user-attachments/assets/020bf5b2-6c69-44bf-ba1f-39389d388d27" />
18
+
19
+ ## Table of Contents
20
+
21
+ - [Quick Start](#quick-start)
22
+ - [Installation](#installation)
23
+ - [Features](#features)
24
+ - [Command Line Usage](#command-line-usage)
25
+ - [About pydantic-resolve](#about-pydantic-resolve)
26
+ - [Development](#development)
27
+ - [Dependencies](#dependencies)
28
+ - [Credits](#credits)
29
+
30
+ ## Quick Start
31
+
32
+ With simple configuration, fastapi-voyager can be embedded into FastAPI:
33
+
34
+ ```python
35
+ from fastapi import FastAPI
36
+ from fastapi_voyager import create_voyager
37
+
38
+ app = FastAPI()
39
+
40
+ app.mount('/voyager',
41
+ create_voyager(
42
+ app,
43
+ module_color={'src.services': 'tomato'},
44
+ module_prefix='src.services',
45
+ swagger_url="/docs",
46
+ ga_id="G-XXXXXXXXVL",
47
+ initial_page_policy='first',
48
+ online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master',
49
+ enable_pydantic_resolve_meta=True))
50
+ ```
51
+
52
+ Visit `http://localhost:8000/voyager` to explore your API visually.
53
+
54
+ [View full example](https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48)
55
+
56
+ ## Installation
57
+
58
+ ### Install via pip
59
+
60
+ ```bash
61
+ pip install fastapi-voyager
62
+ ```
63
+
64
+ ### Install via uv
65
+
66
+ ```bash
67
+ uv add fastapi-voyager
68
+ ```
69
+
70
+ ### Run with CLI
71
+
72
+ ```bash
73
+ voyager -m path.to.your.app.module --server
74
+ ```
75
+
76
+ For sub-application scenarios (e.g., `app.mount("/api", api)`), specify the app name:
77
+
78
+ ```bash
79
+ voyager -m path.to.your.app.module --server --app api
80
+ ```
81
+
82
+ > **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.
83
+
84
+ ## Features
85
+
86
+ 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.
87
+
88
+ **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.
89
+
90
+ ### Highlight Nodes and Links
91
+
92
+ Click a node to highlight its upstream and downstream nodes. Figure out the related models of one page, or how many pages are related with one model.
93
+
94
+ <img width="1100" height="700" alt="highlight nodes and dependencies" src="https://github.com/user-attachments/assets/3e0369ea-5fa4-469a-82c1-ed57d407e53d" />
95
+
96
+ ### View Source Code
97
+
98
+ Double-click a node or route to show source code or open the file in VSCode.
99
+
100
+ <img width="1297" height="940" alt="view source code" src="https://github.com/user-attachments/assets/c8bb2e7d-b727-42a6-8c9e-64dce297d2d8" />
101
+
102
+ ### Quick Search
103
+
104
+ Search schemas by name and display their upstream and downstream dependencies. Use `Shift + Click` on any node to quickly search for it.
105
+
106
+ <img width="1587" height="873" alt="quick search functionality" src="https://github.com/user-attachments/assets/ee4716f3-233d-418f-bc0e-3b214d1498f7" />
107
+
108
+ ### Display ER Diagram
109
+
110
+ ER diagram is a feature from pydantic-resolve which provides a solid expression for business descriptions. You can visualize application-level entity relationship diagrams.
111
+
112
+ ```python
113
+ from pydantic_resolve import ErDiagram, Entity, Relationship
114
+
115
+ diagram = ErDiagram(
116
+ configs=[
117
+ Entity(
118
+ kls=Team,
119
+ relationships=[
120
+ Relationship(field='id', target_kls=list[Sprint], loader=sprint_loader.team_to_sprint_loader),
121
+ Relationship(field='id', target_kls=list[User], loader=user_loader.team_to_user_loader)
122
+ ]
123
+ ),
124
+ Entity(
125
+ kls=Sprint,
126
+ relationships=[
127
+ Relationship(field='id', target_kls=list[Story], loader=story_loader.sprint_to_story_loader)
128
+ ]
129
+ ),
130
+ Entity(
131
+ kls=Story,
132
+ relationships=[
133
+ Relationship(field='id', target_kls=list[Task], loader=task_loader.story_to_task_loader),
134
+ Relationship(field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
135
+ ]
136
+ ),
137
+ Entity(
138
+ kls=Task,
139
+ relationships=[
140
+ Relationship(field='owner_id', target_kls=User, loader=user_loader.user_batch_loader)
141
+ ]
142
+ )
143
+ ]
144
+ )
145
+
146
+ # Display in voyager
147
+ app.mount('/voyager',
148
+ create_voyager(
149
+ app,
150
+ er_diagram=diagram
151
+ ))
152
+ ```
153
+
154
+ <img width="1276" height="613" alt="ER diagram visualization" src="https://github.com/user-attachments/assets/ea0091bb-ee11-4f71-8be3-7129d956c910" />
155
+
156
+ ### Show Pydantic Resolve Meta Info
157
+
158
+ Set `enable_pydantic_resolve_meta=True` in `create_voyager`, then toggle the "pydantic resolve meta" button to visualize resolve/post/expose/collect operations.
159
+
160
+ <img width="1604" height="535" alt="pydantic resolve meta information" src="https://github.com/user-attachments/assets/d1639555-af41-4a08-9970-4b8ef314596a" />
161
+
162
+ ## Command Line Usage
163
+
164
+ ### Start Server
165
+
166
+ ```bash
167
+ # Open in browser (default port 8000)
168
+ voyager -m tests.demo --server
169
+
170
+ # Custom port
171
+ voyager -m tests.demo --server --port=8002
172
+
173
+ # Specify app name
174
+ voyager -m tests.demo --server --app my_app
175
+ ```
176
+
177
+ ### Generate DOT File
178
+
179
+ ```bash
180
+ # Generate .dot file
181
+ voyager -m tests.demo
182
+
183
+ # Specify app
184
+ voyager -m tests.demo --app my_app
185
+
186
+ # Filter by schema
187
+ voyager -m tests.demo --schema Task
188
+
189
+ # Show all fields
190
+ voyager -m tests.demo --show_fields all
191
+
192
+ # Custom module colors
193
+ voyager -m tests.demo --module_color=tests.demo:red --module_color=tests.service:tomato
194
+
195
+ # Output to file
196
+ voyager -m tests.demo -o my_visualization.dot
197
+
198
+ # Version and help
199
+ voyager --version
200
+ voyager --help
201
+ ```
202
+
203
+ ## About pydantic-resolve
204
+
205
+ pydantic-resolve is a lightweight tool designed to build complex, nested data in a simple, declarative way. In v2, it introduced an important feature: **ER Diagram**, and fastapi-voyager has supported this feature, allowing for a clearer understanding of business relationships.
206
+
207
+ The ~~`@ensure_subset` decorator~~ `DefineSubset` metaclass helps safely pick fields from the 'source class' while **indicating the reference** from the current class to the base class.
208
+
209
+ Developers can use fastapi-voyager without needing to know anything about pydantic-resolve, but I still highly recommend everyone to give it a try.
210
+
211
+ ## Development
212
+
213
+ ### Setup Development Environment
214
+
215
+ ```bash
216
+ # Fork and clone the repository
217
+ git clone https://github.com/your-username/fastapi-voyager.git
218
+ cd fastapi-voyager
219
+
220
+ # Install uv
221
+ curl -LsSf https://astral.sh/uv/install.sh | sh
222
+
223
+ # Create virtual environment and install dependencies
224
+ uv venv
225
+ source .venv/bin/activate
226
+ uv pip install ".[dev]"
227
+
228
+ # Run development server
229
+ uvicorn tests.programatic:app --reload
230
+ ```
231
+
232
+ Visit `http://localhost:8000/voyager` to see changes.
233
+
234
+ ### Setup Git Hooks (Optional)
235
+
236
+ Enable automatic code formatting before commits:
237
+
238
+ ```bash
239
+ ./setup-hooks.sh
240
+ # or manually:
241
+ git config core.hooksPath .githooks
242
+ ```
243
+
244
+ This will run Prettier automatically before each commit. See [`.githooks/README.md`](./.githooks/README.md) for details.
245
+
246
+ ### Project Structure
247
+
248
+ **Frontend:**
249
+ - `src/fastapi_voyager/web/vue-main.js` - Main JavaScript entry
250
+
251
+ **Backend:**
252
+ - `voyager.py` - Main entry point
253
+ - `render.py` - Generate DOT files
254
+ - `server.py` - Server mode
255
+
256
+ ## Roadmap
257
+
258
+ - [Ideas](./docs/idea.md)
259
+ - [Changelog & Roadmap](./docs/changelog.md)
260
+
261
+ ## Dependencies
262
+
263
+ - [FastAPI](https://fastapi.tiangolo.com/)
264
+ - [pydantic-resolve](https://github.com/allmonday/pydantic-resolve)
265
+ - [Quasar Framework](https://quasar.dev/)
266
+
267
+ ## Credits
268
+
269
+ - [graphql-voyager](https://apis.guru/graphql-voyager/) - Thanks for inspiration
270
+ - [vscode-interactive-graphviz](https://github.com/tintinweb/vscode-interactive-graphviz) - Thanks for web visualization
271
+
272
+ ## License
273
+
274
+ MIT License
@@ -167,8 +167,19 @@
167
167
  - [x] refactor vue-main.js, move methods to store
168
168
  - [x] optimize search flow
169
169
  - 0.15.4
170
- - [ ] add tests
170
+ - [x] static files cache buster
171
+ - [x] store voyager/erd toggle value in url query string
172
+ - [x] set highlight style
173
+ - 0.15.5
174
+ - [x] fix loadInitial bug
175
+
176
+ ## 0.16, enhance er diagram
177
+ - 0.16.0
178
+ - [ ] show loader name
179
+ - [ ] show relationship list when double click entity in er diagram
180
+ - [ ] highlight entity in use case
171
181
 
172
182
  ## 1.0, release
183
+ - [ ] add tests
173
184
 
174
185
 
@@ -19,6 +19,7 @@ WEB_DIR = Path(__file__).parent / "web"
19
19
  WEB_DIR.mkdir(exist_ok=True)
20
20
 
21
21
  GA_PLACEHOLDER = "<!-- GA_SNIPPET -->"
22
+ VERSION_PLACEHOLDER = "<!-- VERSION_PLACEHOLDER -->"
22
23
 
23
24
  def _build_ga_snippet(ga_id: str | None) -> str:
24
25
  if not ga_id:
@@ -197,7 +198,9 @@ def create_voyager(
197
198
  index_file = WEB_DIR / "index.html"
198
199
  if index_file.exists():
199
200
  content = index_file.read_text(encoding="utf-8")
200
- return content.replace(GA_PLACEHOLDER, _build_ga_snippet(ga_id))
201
+ content = content.replace(GA_PLACEHOLDER, _build_ga_snippet(ga_id))
202
+ content = content.replace(VERSION_PLACEHOLDER, f"?v={__version__}")
203
+ return content
201
204
  # fallback simple page if index.html missing
202
205
  return """
203
206
  <!doctype html>
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.15.3"
2
+ __version__ = "0.15.5"
@@ -69,10 +69,45 @@ export class GraphUI {
69
69
  }
70
70
 
71
71
  highlightSchemaBanner(node) {
72
+ // Get all polygons in the node
72
73
  const polygons = node.querySelectorAll("polygon")
73
- const ele = polygons[2] // select the second polygon
74
- if (ele) {
75
- ele.setAttribute("stroke-width", "3.5")
74
+
75
+ // The first polygon is typically the outer frame of the entire node
76
+ const outerFrame = polygons[0]
77
+ // The second polygon is typically the title background
78
+ const titleBg = polygons[1]
79
+
80
+ if (outerFrame) {
81
+ // Save original attributes for potential restoration
82
+ if (!outerFrame.hasAttribute("data-original-stroke")) {
83
+ outerFrame.setAttribute("data-original-stroke", outerFrame.getAttribute("stroke") || "")
84
+ outerFrame.setAttribute(
85
+ "data-original-stroke-width",
86
+ outerFrame.getAttribute("stroke-width") || "1"
87
+ )
88
+ outerFrame.setAttribute("data-original-fill", outerFrame.getAttribute("fill") || "")
89
+ }
90
+
91
+ // Apply bold purple border to the outer frame
92
+ outerFrame.setAttribute("stroke", "#822dba")
93
+ outerFrame.setAttribute("stroke-width", "3.0")
94
+ }
95
+
96
+ if (titleBg) {
97
+ // Save original attributes
98
+ if (!titleBg.hasAttribute("data-original-stroke")) {
99
+ titleBg.setAttribute("data-original-stroke", titleBg.getAttribute("stroke") || "")
100
+ titleBg.setAttribute(
101
+ "data-original-stroke-width",
102
+ titleBg.getAttribute("stroke-width") || "1"
103
+ )
104
+ titleBg.setAttribute("data-original-fill", titleBg.getAttribute("fill") || "")
105
+ }
106
+
107
+ // Apply purple background to title
108
+ titleBg.setAttribute("fill", "#822dba")
109
+ // Also update the stroke to match
110
+ titleBg.setAttribute("stroke", "#822dba")
76
111
  }
77
112
  }
78
113