fastapi-router-versioning 0.2.0__tar.gz → 0.2.2__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.
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/PKG-INFO +23 -1
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/README.md +22 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/RELEASE_NOTES.md +21 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/calver_app.py +14 -0
- fastapi_router_versioning-0.2.2/examples/mounted_subapps_app.py +76 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/multi_router_app.py +14 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/openapi_hook_app.py +14 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/self_hosted_docs_app.py +14 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/semver_app.py +14 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/semver_major_only_app.py +14 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/webhook_versioning_app.py +14 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/fastapi_router_versioning/__init__.py +1 -1
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/fastapi_router_versioning/versioner.py +229 -199
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/tests/test_versioner.py +207 -2
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.github/dependabot.yml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.github/workflows/detect-conflicts.yml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.github/workflows/pre-commit.yml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.github/workflows/prepare-release.yml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.github/workflows/publish.yml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.github/workflows/test-redistribute.yml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.github/workflows/test.yml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.gitignore +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.pre-commit-config.yaml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/.python-version +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/LICENSE +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/download_static_assets.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/validation_error_code_app.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/validation_error_code_custom_handler_app.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/fastapi_router_versioning/py.typed +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/pyproject.toml +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/scripts/format.sh +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/scripts/lint.sh +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/scripts/prepare_release.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/scripts/test-cov-html.sh +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/scripts/test-cov.sh +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/scripts/test.sh +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/tests/__init__.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/tests/test_calver.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/tests/test_fastapi_integration.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/tests/test_prepare_release.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/tests/test_semver.py +0 -0
- {fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-router-versioning
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Router-based API versioning for FastAPI, with per-version docs and a declarative route lifecycle.
|
|
5
5
|
Project-URL: Homepage, https://github.com/mat81black/fastapi-router-versioning
|
|
6
6
|
Project-URL: Repository, https://github.com/mat81black/fastapi-router-versioning
|
|
@@ -400,6 +400,23 @@ RouterVersioner(
|
|
|
400
400
|
).versionize()
|
|
401
401
|
```
|
|
402
402
|
|
|
403
|
+
The app's own root `/docs`, `/redoc`, and `/openapi.json` (not versioned) also reflect
|
|
404
|
+
`validation_error_code` — RouterVersioner patches `app.openapi()` itself, so nothing needs to
|
|
405
|
+
be disabled to get consistent schemas everywhere.
|
|
406
|
+
|
|
407
|
+
**If you do share one app across multiple `RouterVersioner` instances** — the only case this is
|
|
408
|
+
actually needed for is mixing `version_format` (e.g. SemVer for one group of routes, CalVer for
|
|
409
|
+
another) on the same app, since a single instance already handles multiple routers and prefixes
|
|
410
|
+
via `routers=[...]` (see [`multi_router_app.py`](examples/multi_router_app.py)). All instances
|
|
411
|
+
must use the same `validation_error_code` when `handle_validation_exceptions=True` — since
|
|
412
|
+
FastAPI's exception handler is app-wide, not per-router, a mismatch raises `RuntimeError` at
|
|
413
|
+
construction time instead of silently picking whichever was registered first. Each instance
|
|
414
|
+
must also use a distinct `prefix_format`/`latest_prefix`, otherwise their docs/openapi routes
|
|
415
|
+
would collide at the same path — this also raises `RuntimeError`.
|
|
416
|
+
|
|
417
|
+
For genuine module independence (separate `validation_error_code`, no risk of prefix collisions,
|
|
418
|
+
isolated docs), mount each module as its own FastAPI sub-application instead — see below.
|
|
419
|
+
|
|
403
420
|
### Reverse proxy / sub-app mounting
|
|
404
421
|
|
|
405
422
|
When the app runs behind a reverse proxy or is mounted as a sub-application, the ASGI `root_path` is included in all per-version doc URLs automatically — no extra configuration needed:
|
|
@@ -410,6 +427,10 @@ parent.mount("/api", app) # root_path="/api" is injected at request time
|
|
|
410
427
|
# /api/v1_0/docs correctly references /api/v1_0/openapi.json
|
|
411
428
|
```
|
|
412
429
|
|
|
430
|
+
Each sub-application has its own independent `RouterVersioner`, `app.state`, and OpenAPI docs —
|
|
431
|
+
no coordination needed between them, including different `validation_error_code` per
|
|
432
|
+
sub-application. See [`examples/mounted_subapps_app.py`](examples/mounted_subapps_app.py).
|
|
433
|
+
|
|
413
434
|
### Callback hook
|
|
414
435
|
|
|
415
436
|
Run custom logic each time a versioned router is created — useful for logging or metrics:
|
|
@@ -442,6 +463,7 @@ Runnable examples are available in the [`examples/`](examples/) directory:
|
|
|
442
463
|
| [`self_hosted_docs_app.py`](examples/self_hosted_docs_app.py) | Swagger UI and ReDoc from local static assets |
|
|
443
464
|
| [`validation_error_code_app.py`](examples/validation_error_code_app.py) | Return `400` instead of `422` for validation errors (automatic handler via `RouterVersioner`) |
|
|
444
465
|
| [`validation_error_code_custom_handler_app.py`](examples/validation_error_code_custom_handler_app.py) | Same, but with `handle_validation_exceptions=False` and a user-defined exception handler |
|
|
466
|
+
| [`mounted_subapps_app.py`](examples/mounted_subapps_app.py) | Independently versioned modules as separate FastAPI sub-applications via `app.mount()` |
|
|
445
467
|
|
|
446
468
|
---
|
|
447
469
|
|
|
@@ -371,6 +371,23 @@ RouterVersioner(
|
|
|
371
371
|
).versionize()
|
|
372
372
|
```
|
|
373
373
|
|
|
374
|
+
The app's own root `/docs`, `/redoc`, and `/openapi.json` (not versioned) also reflect
|
|
375
|
+
`validation_error_code` — RouterVersioner patches `app.openapi()` itself, so nothing needs to
|
|
376
|
+
be disabled to get consistent schemas everywhere.
|
|
377
|
+
|
|
378
|
+
**If you do share one app across multiple `RouterVersioner` instances** — the only case this is
|
|
379
|
+
actually needed for is mixing `version_format` (e.g. SemVer for one group of routes, CalVer for
|
|
380
|
+
another) on the same app, since a single instance already handles multiple routers and prefixes
|
|
381
|
+
via `routers=[...]` (see [`multi_router_app.py`](examples/multi_router_app.py)). All instances
|
|
382
|
+
must use the same `validation_error_code` when `handle_validation_exceptions=True` — since
|
|
383
|
+
FastAPI's exception handler is app-wide, not per-router, a mismatch raises `RuntimeError` at
|
|
384
|
+
construction time instead of silently picking whichever was registered first. Each instance
|
|
385
|
+
must also use a distinct `prefix_format`/`latest_prefix`, otherwise their docs/openapi routes
|
|
386
|
+
would collide at the same path — this also raises `RuntimeError`.
|
|
387
|
+
|
|
388
|
+
For genuine module independence (separate `validation_error_code`, no risk of prefix collisions,
|
|
389
|
+
isolated docs), mount each module as its own FastAPI sub-application instead — see below.
|
|
390
|
+
|
|
374
391
|
### Reverse proxy / sub-app mounting
|
|
375
392
|
|
|
376
393
|
When the app runs behind a reverse proxy or is mounted as a sub-application, the ASGI `root_path` is included in all per-version doc URLs automatically — no extra configuration needed:
|
|
@@ -381,6 +398,10 @@ parent.mount("/api", app) # root_path="/api" is injected at request time
|
|
|
381
398
|
# /api/v1_0/docs correctly references /api/v1_0/openapi.json
|
|
382
399
|
```
|
|
383
400
|
|
|
401
|
+
Each sub-application has its own independent `RouterVersioner`, `app.state`, and OpenAPI docs —
|
|
402
|
+
no coordination needed between them, including different `validation_error_code` per
|
|
403
|
+
sub-application. See [`examples/mounted_subapps_app.py`](examples/mounted_subapps_app.py).
|
|
404
|
+
|
|
384
405
|
### Callback hook
|
|
385
406
|
|
|
386
407
|
Run custom logic each time a versioned router is created — useful for logging or metrics:
|
|
@@ -413,6 +434,7 @@ Runnable examples are available in the [`examples/`](examples/) directory:
|
|
|
413
434
|
| [`self_hosted_docs_app.py`](examples/self_hosted_docs_app.py) | Swagger UI and ReDoc from local static assets |
|
|
414
435
|
| [`validation_error_code_app.py`](examples/validation_error_code_app.py) | Return `400` instead of `422` for validation errors (automatic handler via `RouterVersioner`) |
|
|
415
436
|
| [`validation_error_code_custom_handler_app.py`](examples/validation_error_code_custom_handler_app.py) | Same, but with `handle_validation_exceptions=False` and a user-defined exception handler |
|
|
437
|
+
| [`mounted_subapps_app.py`](examples/mounted_subapps_app.py) | Independently versioned modules as separate FastAPI sub-applications via `app.mount()` |
|
|
416
438
|
|
|
417
439
|
---
|
|
418
440
|
|
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## Latest Changes
|
|
4
4
|
|
|
5
|
+
## 0.2.2 (2026-07-01)
|
|
6
|
+
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
* 🐛 Fix root OpenAPI schema not reflecting validation_error_code. PR [#21](https://github.com/mat81black/fastapi-router-versioning/pull/21) by [@mat81black](https://github.com/mat81black).
|
|
10
|
+
* 🐛 Fix silent conflicts from misconfigured RouterVersioner instances sharing an app. PR [#20](https://github.com/mat81black/fastapi-router-versioning/pull/20) by [@mat81black](https://github.com/mat81black).
|
|
11
|
+
|
|
12
|
+
### Internal
|
|
13
|
+
|
|
14
|
+
* 👷 Add a regression test for `RouterVersioner` under a real `app.mount()` sub-application, and `examples/mounted_subapps_app.py` demonstrating the same scenario. PR [#22](https://github.com/mat81black/fastapi-router-versioning/pull/22) by [@mat81black](https://github.com/mat81black).
|
|
15
|
+
|
|
16
|
+
### Refactors
|
|
17
|
+
|
|
18
|
+
* ♻️ Reorder RouterVersioner methods to match their call graph. PR [#23](https://github.com/mat81black/fastapi-router-versioning/pull/23) by [@mat81black](https://github.com/mat81black).
|
|
19
|
+
|
|
20
|
+
## 0.2.1 (2026-06-26)
|
|
21
|
+
|
|
22
|
+
### Fixes
|
|
23
|
+
|
|
24
|
+
* 🐛 Fix `api_version` signature to enforce `deprecate_in` and `remove_in` as keyword-only arguments, aligning the implementation with the documented signature. PR [#18](https://github.com/mat81black/fastapi-router-versioning/pull/18) by [@mat81black](https://github.com/mat81black).
|
|
25
|
+
|
|
5
26
|
## 0.2.0 (2026-06-26)
|
|
6
27
|
|
|
7
28
|
### Features
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from fastapi import APIRouter, FastAPI
|
|
2
|
+
from pydantic import BaseModel
|
|
2
3
|
|
|
3
4
|
from fastapi_router_versioning import RouterVersioner, VersionFormat, api_version
|
|
4
5
|
|
|
@@ -10,6 +11,11 @@ app = FastAPI(
|
|
|
10
11
|
router = APIRouter()
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
class CreateItemRequest(BaseModel):
|
|
15
|
+
name: str
|
|
16
|
+
quantity: int
|
|
17
|
+
|
|
18
|
+
|
|
13
19
|
# 1. Introduced in the January release, persists across all versions
|
|
14
20
|
@router.get("/persistent")
|
|
15
21
|
@api_version("2025-01-01")
|
|
@@ -41,6 +47,14 @@ def future_route() -> dict[str, str]:
|
|
|
41
47
|
return {"status": "active", "message": "Welcome to the December release!"}
|
|
42
48
|
|
|
43
49
|
|
|
50
|
+
# POST /items with an invalid "quantity" (e.g. "not-a-number") returns FastAPI's
|
|
51
|
+
# default 422 validation error.
|
|
52
|
+
@router.post("/items")
|
|
53
|
+
@api_version("2025-01-01")
|
|
54
|
+
def create_item(body: CreateItemRequest) -> dict[str, str]:
|
|
55
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
56
|
+
|
|
57
|
+
|
|
44
58
|
versioner = RouterVersioner(
|
|
45
59
|
app=app,
|
|
46
60
|
routers=router,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sub-applications example.
|
|
3
|
+
|
|
4
|
+
Shows how to mount independently versioned modules as separate FastAPI
|
|
5
|
+
sub-applications via app.mount() — each with its own RouterVersioner, docs,
|
|
6
|
+
and OpenAPI schema. RouterVersioner already threads the ASGI root_path through
|
|
7
|
+
docs/openapi URLs automatically, so no extra configuration is needed.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter, FastAPI
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
from fastapi_router_versioning import RouterVersioner, VersionFormat, api_version
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CreateItemRequest(BaseModel):
|
|
17
|
+
name: str
|
|
18
|
+
quantity: int
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
main_app = FastAPI(title="Main App")
|
|
22
|
+
|
|
23
|
+
# ── Admin sub-app: SemVer, validation errors return 400 ──────────────────────
|
|
24
|
+
|
|
25
|
+
admin_app = FastAPI(title="Admin API")
|
|
26
|
+
admin_router = APIRouter()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@admin_router.get("/users")
|
|
30
|
+
@api_version((1, 0))
|
|
31
|
+
def list_admin_users() -> dict[str, list[str]]:
|
|
32
|
+
return {"users": []}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@admin_router.post("/items")
|
|
36
|
+
@api_version((1, 0))
|
|
37
|
+
def create_admin_item(body: CreateItemRequest) -> dict[str, str]:
|
|
38
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
RouterVersioner(
|
|
42
|
+
app=admin_app, routers=admin_router, version_format=VersionFormat.SEMVER, validation_error_code=400
|
|
43
|
+
).versionize()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ── Orders sub-app: CalVer ────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
orders_app = FastAPI(title="Orders API")
|
|
49
|
+
orders_router = APIRouter()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@orders_router.get("/")
|
|
53
|
+
@api_version("2025-01-01")
|
|
54
|
+
def list_orders() -> dict[str, list[str]]:
|
|
55
|
+
return {"orders": []}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@orders_router.post("/items")
|
|
59
|
+
@api_version("2025-01-01")
|
|
60
|
+
def create_order_item(body: CreateItemRequest) -> dict[str, str]:
|
|
61
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
RouterVersioner(app=orders_app, routers=orders_router, version_format=VersionFormat.CALVER).versionize()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ── Mount both sub-apps on the main app ───────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
main_app.mount("/admin", admin_app)
|
|
70
|
+
main_app.mount("/orders", orders_app)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
import uvicorn
|
|
75
|
+
|
|
76
|
+
uvicorn.run(main_app, host="127.0.0.1", port=8009)
|
{fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/multi_router_app.py
RENAMED
|
@@ -8,6 +8,7 @@ versioned together under a single prefix tree.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from fastapi import APIRouter, FastAPI
|
|
11
|
+
from pydantic import BaseModel
|
|
11
12
|
|
|
12
13
|
from fastapi_router_versioning import RouterVersioner, VersionFormat, api_version
|
|
13
14
|
|
|
@@ -20,6 +21,11 @@ users_router = APIRouter(prefix="/users", tags=["Users"])
|
|
|
20
21
|
products_router = APIRouter(prefix="/products", tags=["Products"])
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
class CreateProductRequest(BaseModel):
|
|
25
|
+
name: str
|
|
26
|
+
quantity: int
|
|
27
|
+
|
|
28
|
+
|
|
23
29
|
@users_router.get("/")
|
|
24
30
|
@api_version((1, 0))
|
|
25
31
|
def list_users() -> dict[str, list[str]]:
|
|
@@ -44,6 +50,14 @@ def search_products() -> dict[str, list[str]]:
|
|
|
44
50
|
return {"results": []}
|
|
45
51
|
|
|
46
52
|
|
|
53
|
+
# POST /products with an invalid "quantity" (e.g. "not-a-number") returns FastAPI's
|
|
54
|
+
# default 422 validation error.
|
|
55
|
+
@products_router.post("/")
|
|
56
|
+
@api_version((1, 0))
|
|
57
|
+
def create_product(body: CreateProductRequest) -> dict[str, str]:
|
|
58
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
59
|
+
|
|
60
|
+
|
|
47
61
|
versioner = RouterVersioner(
|
|
48
62
|
app=app,
|
|
49
63
|
routers=[users_router, products_router], # list of routers versioned together
|
{fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/openapi_hook_app.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter, FastAPI
|
|
4
|
+
from pydantic import BaseModel
|
|
4
5
|
|
|
5
6
|
from fastapi_router_versioning import RouterVersioner, VersionFormat, VersionT, api_version
|
|
6
7
|
|
|
@@ -12,6 +13,11 @@ app = FastAPI(
|
|
|
12
13
|
router = APIRouter()
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
class CreateItemRequest(BaseModel):
|
|
17
|
+
name: str
|
|
18
|
+
quantity: int
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
@router.get("/items")
|
|
16
22
|
@api_version((1, 0))
|
|
17
23
|
def get_items_v1() -> dict[str, object]:
|
|
@@ -30,6 +36,14 @@ def get_users() -> dict[str, object]:
|
|
|
30
36
|
return {"users": ["alice", "bob"]}
|
|
31
37
|
|
|
32
38
|
|
|
39
|
+
# POST /items with an invalid "quantity" (e.g. "not-a-number") returns FastAPI's
|
|
40
|
+
# default 422 validation error.
|
|
41
|
+
@router.post("/items")
|
|
42
|
+
@api_version((1, 0))
|
|
43
|
+
def create_item(body: CreateItemRequest) -> dict[str, str]:
|
|
44
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
45
|
+
|
|
46
|
+
|
|
33
47
|
def my_openapi_hook(schema: dict[str, Any], version: VersionT) -> dict[str, Any]:
|
|
34
48
|
# Applied to every version: add a company logo to the docs
|
|
35
49
|
schema["info"]["x-logo"] = {"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"}
|
{fastapi_router_versioning-0.2.0 → fastapi_router_versioning-0.2.2}/examples/self_hosted_docs_app.py
RENAMED
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
|
|
19
19
|
from fastapi import APIRouter, FastAPI
|
|
20
20
|
from fastapi.staticfiles import StaticFiles
|
|
21
|
+
from pydantic import BaseModel
|
|
21
22
|
|
|
22
23
|
from fastapi_router_versioning import RouterVersioner, VersionFormat, api_version
|
|
23
24
|
|
|
@@ -32,12 +33,25 @@ app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
|
|
32
33
|
router = APIRouter()
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
class CreateItemRequest(BaseModel):
|
|
37
|
+
name: str
|
|
38
|
+
quantity: int
|
|
39
|
+
|
|
40
|
+
|
|
35
41
|
@router.get("/items")
|
|
36
42
|
@api_version((1, 0))
|
|
37
43
|
def list_items() -> dict[str, list[str]]:
|
|
38
44
|
return {"items": []}
|
|
39
45
|
|
|
40
46
|
|
|
47
|
+
# POST /items with an invalid "quantity" (e.g. "not-a-number") returns FastAPI's
|
|
48
|
+
# default 422 validation error.
|
|
49
|
+
@router.post("/items")
|
|
50
|
+
@api_version((1, 0))
|
|
51
|
+
def create_item(body: CreateItemRequest) -> dict[str, str]:
|
|
52
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
53
|
+
|
|
54
|
+
|
|
41
55
|
versioner = RouterVersioner(
|
|
42
56
|
app=app,
|
|
43
57
|
routers=router,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from fastapi import APIRouter, FastAPI
|
|
2
|
+
from pydantic import BaseModel
|
|
2
3
|
|
|
3
4
|
from fastapi_router_versioning import RouterVersioner, VersionFormat, api_version
|
|
4
5
|
|
|
@@ -10,6 +11,11 @@ app = FastAPI(
|
|
|
10
11
|
router = APIRouter()
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
class CreateItemRequest(BaseModel):
|
|
15
|
+
name: str
|
|
16
|
+
quantity: int
|
|
17
|
+
|
|
18
|
+
|
|
13
19
|
# 1. Introduced in v1.0, persists across all versions
|
|
14
20
|
@router.get("/persistent")
|
|
15
21
|
@api_version((1, 0))
|
|
@@ -41,6 +47,14 @@ def future_route() -> dict[str, str]:
|
|
|
41
47
|
return {"status": "active", "message": "Welcome to v3.0!"}
|
|
42
48
|
|
|
43
49
|
|
|
50
|
+
# POST /items with an invalid "quantity" (e.g. "not-a-number") returns FastAPI's
|
|
51
|
+
# default 422 validation error.
|
|
52
|
+
@router.post("/items")
|
|
53
|
+
@api_version((1, 0))
|
|
54
|
+
def create_item(body: CreateItemRequest) -> dict[str, str]:
|
|
55
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
56
|
+
|
|
57
|
+
|
|
44
58
|
versioner = RouterVersioner(
|
|
45
59
|
app=app,
|
|
46
60
|
routers=router,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from fastapi import APIRouter, FastAPI
|
|
2
|
+
from pydantic import BaseModel
|
|
2
3
|
|
|
3
4
|
from fastapi_router_versioning import RouterVersioner, VersionFormat, api_version
|
|
4
5
|
|
|
@@ -10,6 +11,11 @@ app = FastAPI(
|
|
|
10
11
|
router = APIRouter()
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
class CreateItemRequest(BaseModel):
|
|
15
|
+
name: str
|
|
16
|
+
quantity: int
|
|
17
|
+
|
|
18
|
+
|
|
13
19
|
# 1. Introduced in v1, persists across all versions
|
|
14
20
|
@router.get("/persistent")
|
|
15
21
|
@api_version((1, 0))
|
|
@@ -41,6 +47,14 @@ def future_route() -> dict[str, str]:
|
|
|
41
47
|
return {"status": "active", "message": "Welcome to v3!"}
|
|
42
48
|
|
|
43
49
|
|
|
50
|
+
# POST /items with an invalid "quantity" (e.g. "not-a-number") returns FastAPI's
|
|
51
|
+
# default 422 validation error.
|
|
52
|
+
@router.post("/items")
|
|
53
|
+
@api_version((1, 0))
|
|
54
|
+
def create_item(body: CreateItemRequest) -> dict[str, str]:
|
|
55
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
56
|
+
|
|
57
|
+
|
|
44
58
|
versioner = RouterVersioner(
|
|
45
59
|
app=app,
|
|
46
60
|
routers=router,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from fastapi import APIRouter, FastAPI
|
|
2
|
+
from pydantic import BaseModel
|
|
2
3
|
|
|
3
4
|
from fastapi_router_versioning import RouterVersioner, VersionFormat, api_version
|
|
4
5
|
|
|
@@ -12,6 +13,11 @@ app = FastAPI(
|
|
|
12
13
|
router = APIRouter()
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
class CreateItemRequest(BaseModel):
|
|
17
|
+
name: str
|
|
18
|
+
quantity: int
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
@router.post("/orders")
|
|
16
22
|
@api_version((1, 0))
|
|
17
23
|
def create_order_v1() -> dict[str, str]:
|
|
@@ -30,6 +36,14 @@ def get_items() -> dict[str, object]:
|
|
|
30
36
|
return {"items": ["a", "b"]}
|
|
31
37
|
|
|
32
38
|
|
|
39
|
+
# POST /items with an invalid "quantity" (e.g. "not-a-number") returns FastAPI's
|
|
40
|
+
# default 422 validation error.
|
|
41
|
+
@router.post("/items")
|
|
42
|
+
@api_version((1, 0))
|
|
43
|
+
def create_item(body: CreateItemRequest) -> dict[str, str]:
|
|
44
|
+
return {"name": body.name, "quantity": str(body.quantity)}
|
|
45
|
+
|
|
46
|
+
|
|
33
47
|
# --- Webhook definitions ---
|
|
34
48
|
# Webhooks document outbound calls your API makes to subscriber URLs.
|
|
35
49
|
# Annotate them with @api_version just like regular routes.
|