mirror of https://github.com/tiangolo/fastapi.git
feat: add scalar integration
This commit is contained in:
parent
d8449b2fff
commit
a1f31ea018
20
README.md
20
README.md
|
|
@ -262,12 +262,20 @@ You will see the automatic interactive API documentation (provided by <a href="h
|
|||
|
||||
### Alternative API docs
|
||||
|
||||
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
There are two alternatives to Swagger UI: Redocly & Scalar.
|
||||
|
||||
Go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
You will see the alternative automatic documentation (provided by <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
|
||||
|
||||

|
||||
|
||||
And now, go to <a href="http://127.0.0.1:8000/scalar" class="external-link" target="_blank">http://127.0.0.1:8000/scalar</a>.
|
||||
|
||||
You will see the alternative automatic documentation (provided by <a href="https://github.com/scalar/scalar" class="external-link" target="_blank">Scalar</a>):
|
||||
|
||||

|
||||
|
||||
## Example upgrade
|
||||
|
||||
Now modify the file `main.py` to receive a body from a `PUT` request.
|
||||
|
|
@ -324,12 +332,20 @@ Now go to <a href="http://127.0.0.1:8000/docs" class="external-link" target="_bl
|
|||
|
||||
### Alternative API docs upgrade
|
||||
|
||||
And now, go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
There are two alternatives to Swagger UI: Redocly & Scalar.
|
||||
|
||||
Go to <a href="http://127.0.0.1:8000/redoc" class="external-link" target="_blank">http://127.0.0.1:8000/redoc</a>.
|
||||
|
||||
* The alternative documentation will also reflect the new query parameter and body:
|
||||
|
||||

|
||||
|
||||
And now, go to <a href="http://127.0.0.1:8000/scalar" class="external-link" target="_blank">http://127.0.0.1:8000/scalar</a>.
|
||||
|
||||
* The alternative documentation will also reflect the new query parameter and body:
|
||||
|
||||

|
||||
|
||||
### Recap
|
||||
|
||||
In summary, you declare **once** the types of parameters, body, etc. as function parameters.
|
||||
|
|
|
|||
|
|
@ -24,10 +24,15 @@ Interactive API documentation and exploration web user interfaces. As the framew
|
|||
|
||||

|
||||
|
||||
* Alternative API documentation with <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
|
||||
* Alternative API documentation with Redocly or Scalar
|
||||
* <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank"><strong>ReDoc</strong></a>.
|
||||
|
||||

|
||||
|
||||
* <a href="https://github.com/scalar/scalar" class="external-link" target="_blank"><strong>Scalar</strong></a>.
|
||||
|
||||

|
||||
|
||||
### Just Modern Python
|
||||
|
||||
It's all based on standard **Python 3.6 type** declarations (thanks to Pydantic). No new syntax to learn. Just standard modern Python.
|
||||
|
|
|
|||
|
|
@ -103,6 +103,10 @@ And **ReDoc** uses the file:
|
|||
|
||||
* <a href="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" class="external-link" target="_blank">`redoc.standalone.js`</a>
|
||||
|
||||
And **Scalar** uses the file:
|
||||
|
||||
* <a href="https://cdn.jsdelivr.net/npm/@scalar/api-reference" class="external-link" target="_blank">`scalar.standalone.js`</a>
|
||||
|
||||
After that, your file structure could look like:
|
||||
|
||||
```
|
||||
|
|
@ -112,6 +116,7 @@ After that, your file structure could look like:
|
|||
│ ├── main.py
|
||||
└── static
|
||||
├── redoc.standalone.js
|
||||
├── scalar.standalone.js
|
||||
├── swagger-ui-bundle.js
|
||||
└── swagger-ui.css
|
||||
```
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 169 KiB |
|
|
@ -1,11 +1,13 @@
|
|||
# OpenAPI `docs`
|
||||
|
||||
Utilities to handle OpenAPI automatic UI documentation, including Swagger UI (by default at `/docs`) and ReDoc (by default at `/redoc`).
|
||||
Utilities to handle OpenAPI automatic UI documentation, including Swagger UI (by default at `/docs`), ReDoc (by default at `/redoc`) & Scalar (by default at `/scalar`).
|
||||
|
||||
::: fastapi.openapi.docs.get_swagger_ui_html
|
||||
|
||||
::: fastapi.openapi.docs.get_redoc_html
|
||||
|
||||
::: fastapi.openapi.docs.get_scalar_html
|
||||
|
||||
::: fastapi.openapi.docs.get_swagger_ui_oauth2_redirect_html
|
||||
|
||||
::: fastapi.openapi.docs.swagger_ui_default_parameters
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ from fastapi import FastAPI
|
|||
from fastapi.openapi.docs import (
|
||||
get_redoc_html,
|
||||
get_swagger_ui_html,
|
||||
get_scalar_html,
|
||||
get_swagger_ui_oauth2_redirect_html,
|
||||
)
|
||||
|
||||
app = FastAPI(docs_url=None, redoc_url=None)
|
||||
app = FastAPI(docs_url=None, redoc_url=None, scalar_url=None)
|
||||
|
||||
|
||||
@app.get("/docs", include_in_schema=False)
|
||||
|
|
@ -32,6 +33,13 @@ async def redoc_html():
|
|||
redoc_js_url="https://unpkg.com/redoc@next/bundles/redoc.standalone.js",
|
||||
)
|
||||
|
||||
@app.get("/scalar", include_in_schema=False)
|
||||
async def scalar_html():
|
||||
return get_scalar_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - Scalar",
|
||||
scalar_js_url="https://cdn.jsdelivr.net/npm/@scalar/api-reference",
|
||||
)
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ from fastapi import FastAPI
|
|||
from fastapi.openapi.docs import (
|
||||
get_redoc_html,
|
||||
get_swagger_ui_html,
|
||||
get_scalar_html,
|
||||
get_swagger_ui_oauth2_redirect_html,
|
||||
)
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
app = FastAPI(docs_url=None, redoc_url=None)
|
||||
app = FastAPI(docs_url=None, redoc_url=None, scalar_url=None)
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
|
@ -35,6 +36,14 @@ async def redoc_html():
|
|||
redoc_js_url="/static/redoc.standalone.js",
|
||||
)
|
||||
|
||||
@app.get("/scalar", include_in_schema=False)
|
||||
async def scalar_html():
|
||||
return get_scalar_html(
|
||||
openapi_url=app.openapi_url,
|
||||
title=app.title + " - Scalar",
|
||||
scalar_js_url="/static/scalar.standalone.js",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ from fastapi.exceptions import RequestValidationError, WebSocketRequestValidatio
|
|||
from fastapi.logger import logger
|
||||
from fastapi.openapi.docs import (
|
||||
get_redoc_html,
|
||||
get_scalar_html,
|
||||
get_swagger_ui_html,
|
||||
get_swagger_ui_oauth2_redirect_html,
|
||||
)
|
||||
|
|
@ -444,6 +445,30 @@ class FastAPI(Starlette):
|
|||
"""
|
||||
),
|
||||
] = "/redoc",
|
||||
scalar_url: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
"""
|
||||
The path to the alternative automatic interactive API documentation
|
||||
provided by ReDoc.
|
||||
|
||||
The default URL is `/scalar`. You can disable it by setting it to `None`.
|
||||
|
||||
If `openapi_url` is set to `None`, this will be automatically disabled.
|
||||
|
||||
Read more in the
|
||||
[FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#docs-urls).
|
||||
|
||||
**Example**
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(docs_url="/documentation", scalar_url="scalar-docs")
|
||||
```
|
||||
"""
|
||||
),
|
||||
] = "/scalar",
|
||||
swagger_ui_oauth2_redirect_url: Annotated[
|
||||
Optional[str],
|
||||
Doc(
|
||||
|
|
@ -833,6 +858,7 @@ class FastAPI(Starlette):
|
|||
self.root_path_in_servers = root_path_in_servers
|
||||
self.docs_url = docs_url
|
||||
self.redoc_url = redoc_url
|
||||
self.scalar_url = scalar_url
|
||||
self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url
|
||||
self.swagger_ui_init_oauth = swagger_ui_init_oauth
|
||||
self.swagger_ui_parameters = swagger_ui_parameters
|
||||
|
|
@ -1047,6 +1073,17 @@ class FastAPI(Starlette):
|
|||
)
|
||||
|
||||
self.add_route(self.redoc_url, redoc_html, include_in_schema=False)
|
||||
|
||||
if self.openapi_url and self.scalar_url:
|
||||
|
||||
async def scalar_html(req: Request) -> HTMLResponse:
|
||||
root_path = req.scope.get("root_path", "").rstrip("/")
|
||||
openapi_url = root_path + self.openapi_url
|
||||
return get_scalar_html(
|
||||
openapi_url=openapi_url, title=self.title + " - Scalar"
|
||||
)
|
||||
|
||||
self.add_route(self.scalar_url, scalar_html, include_in_schema=False)
|
||||
|
||||
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
||||
if self.root_path:
|
||||
|
|
|
|||
|
|
@ -157,6 +157,95 @@ def get_swagger_ui_html(
|
|||
"""
|
||||
return HTMLResponse(html)
|
||||
|
||||
def get_scalar_html(
|
||||
*,
|
||||
openapi_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The OpenAPI URL that Scalar should load and use.
|
||||
|
||||
This is normally done automatically by FastAPI using the default URL
|
||||
`/openapi.json`.
|
||||
"""
|
||||
),
|
||||
],
|
||||
title: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The HTML `<title>` content, normally shown in the browser tab.
|
||||
"""
|
||||
),
|
||||
],
|
||||
scalar_js_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL to use to load the Scalar JavaScript.
|
||||
|
||||
It is normally set to a CDN URL.
|
||||
"""
|
||||
),
|
||||
] = "https://cdn.jsdelivr.net/npm/@scalar/api-reference",
|
||||
scalar_proxy_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL to use to set the Scalar Proxy.
|
||||
|
||||
It is normally set to a Scalar API URL (https://api.scalar.com/request-proxy), but default is empty
|
||||
"""
|
||||
),
|
||||
] = "",
|
||||
scalar_favicon_url: Annotated[
|
||||
str,
|
||||
Doc(
|
||||
"""
|
||||
The URL of the favicon to use. It is normally shown in the browser tab.
|
||||
"""
|
||||
),
|
||||
] = "https://fastapi.tiangolo.com/img/favicon.png",
|
||||
) -> HTMLResponse:
|
||||
"""
|
||||
Generate and return the HTML response that loads Scalar for the alternative
|
||||
API docs (normally served at `/scalar`).
|
||||
|
||||
You would only call this function yourself if you needed to override some parts,
|
||||
for example the URLs to use to load Scalar's JavaScript and CSS.
|
||||
|
||||
Read more about it in the
|
||||
[FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
|
||||
"""
|
||||
html = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{title}</title>
|
||||
<!-- needed for adaptive design -->
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="{scalar_favicon_url}">
|
||||
<style>
|
||||
body {{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
Scalar requires Javascript to function. Please enable it to browse the documentation.
|
||||
</noscript>
|
||||
<script
|
||||
id="api-reference"
|
||||
data-url="{openapi_url}"
|
||||
data-proxy-url="{scalar_proxy_url}"></script>
|
||||
<script src="{scalar_js_url}"></script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(html)
|
||||
|
||||
def get_redoc_html(
|
||||
*,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ def test_redoc():
|
|||
assert response.headers["content-type"] == "text/html; charset=utf-8"
|
||||
assert "redoc@next" in response.text
|
||||
|
||||
def test_scalar():
|
||||
response = client.get("/scalar")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.headers["content-type"] == "text/html; charset=utf-8"
|
||||
assert "@scalar/api-reference" in response.text
|
||||
|
||||
def test_enum_status_code_response():
|
||||
response = client.get("/enum-status-code")
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ def test_redoc_html(client: TestClient):
|
|||
assert response.status_code == 200, response.text
|
||||
assert "https://unpkg.com/redoc@next/bundles/redoc.standalone.js" in response.text
|
||||
|
||||
def test_scalar_html(client: TestClient):
|
||||
response = client.get("/scalar")
|
||||
assert response.status_code == 200, response.text
|
||||
assert "https://cdn.jsdelivr.net/npm/@scalar/api-reference" in response.text
|
||||
|
||||
def test_api(client: TestClient):
|
||||
response = client.get("/users/john")
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ def test_redoc_html(client: TestClient):
|
|||
assert response.status_code == 200, response.text
|
||||
assert "/static/redoc.standalone.js" in response.text
|
||||
|
||||
def test_scalar_html(client: TestClient):
|
||||
response = client.get("/scalar")
|
||||
assert response.status_code == 200, response.text
|
||||
assert "/static/scalar.standalone.js" in response.text
|
||||
|
||||
|
||||
def test_api(client: TestClient):
|
||||
response = client.get("/users/john")
|
||||
|
|
|
|||
Loading…
Reference in New Issue