diff --git a/docs/en/docs/advanced/behind-a-proxy.md b/docs/en/docs/advanced/behind-a-proxy.md
index 660e374a43..8f0cecf4af 100644
--- a/docs/en/docs/advanced/behind-a-proxy.md
+++ b/docs/en/docs/advanced/behind-a-proxy.md
@@ -42,16 +42,19 @@ proxy --> server
!!! tip
The IP `0.0.0.0` is commonly used to mean that the program listens on all the IPs available in that machine/server.
-The docs UI would also need that the JSON payload with the OpenAPI schema has the path defined as `/api/v1/app` (behind the proxy) instead of `/app`. For example, something like:
+The docs UI would also need the OpenAPI schema to declare that this API `server` is located at `/api/v1` (behind the proxy). For example:
-```JSON hl_lines="5"
+```JSON hl_lines="4 5 6 7 8"
{
"openapi": "3.0.2",
// More stuff here
+ "servers": [
+ {
+ "url": "/api/v1"
+ }
+ ],
"paths": {
- "/api/v1/app": {
// More stuff here
- }
}
}
```
@@ -264,15 +267,77 @@ You can check it at
-But if we access the docs UI at the "official" URL using the proxy, at `/api/v1/docs`, it works correctly! 🎉
+But if we access the docs UI at the "official" URL using the proxy with port `9999`, at `/api/v1/docs`, it works correctly! 🎉
+
+You can check it at http://127.0.0.1:9999/api/v1/docs:
+
+
Right as we wanted it. ✔️
-This is because FastAPI uses this `root_path` internally to tell the docs UI to use the URL for OpenAPI with the path prefix provided by `root_path`.
+This is because FastAPI uses this `root_path` to create the default `server` in OpenAPI with the URL provided by `root_path`.
-You can check it at http://127.0.0.1:9999/api/v1/docs:
+## Additional servers
-
+!!! warning
+ This is a more advanced use case. Feel free to skip it.
+
+By default, **FastAPI** will create a `server` in the OpenAPI schema with the URL for the `root_path`.
+
+But you can also provide other alternative `servers`, for example if you want *the same* docs UI to interact with a staging and production environments.
+
+If you pass a custom list of `servers` and there's a `root_path` (because your API lives behind a proxy), **FastAPI** will insert a "server" with this `root_path` at the beginning of the list.
+
+For example:
+
+```Python hl_lines="4 5 6 7"
+{!../../../docs_src/behind_a_proxy/tutorial003.py!}
+```
+
+Will generate an OpenAPI schema like:
+
+```JSON hl_lines="5 6 7"
+{
+ "openapi": "3.0.2",
+ // More stuff here
+ "servers": [
+ {
+ "url": "/api/v1"
+ },
+ {
+ "url": "https://stag.example.com",
+ "description": "Staging environment"
+ },
+ {
+ "url": "https://prod.example.com",
+ "description": "Production environment"
+ }
+ ],
+ "paths": {
+ // More stuff here
+ }
+}
+```
+
+!!! tip
+ Notice the auto-generated server with a `url` value of `/api/v1`, taken from the `root_path`.
+
+In the docs UI at http://127.0.0.1:9999/api/v1/docs it would look like:
+
+
+
+!!! tip
+ The docs UI will interact with the server that you select.
+
+### Disable automatic server from `root_path`
+
+If you don't want **FastAPI** to include an automatic server using the `root_path`, you can use the parameter `root_path_in_servers=False`:
+
+```Python hl_lines="9"
+{!../../../docs_src/behind_a_proxy/tutorial004.py!}
+```
+
+and then it won't include it in the OpenAPI schema.
## Mounting a sub-application
diff --git a/docs/en/docs/advanced/extending-openapi.md b/docs/en/docs/advanced/extending-openapi.md
index 30cd857d5f..4985aebde9 100644
--- a/docs/en/docs/advanced/extending-openapi.md
+++ b/docs/en/docs/advanced/extending-openapi.md
@@ -32,7 +32,6 @@ And that function `get_openapi()` receives as parameters:
* `openapi_version`: The version of the OpenAPI specification used. By default, the latest: `3.0.2`.
* `description`: The description of your API.
* `routes`: A list of routes, these are each of the registered *path operations*. They are taken from `app.routes`.
-* `openapi_prefix`: The URL prefix to be used in your OpenAPI.
## Overriding the defaults
@@ -52,22 +51,15 @@ First, write all your **FastAPI** application as normally:
Then, use the same utility function to generate the OpenAPI schema, inside a `custom_openapi()` function:
-```Python hl_lines="2 15 16 17 18 19 20 21"
+```Python hl_lines="2 15 16 17 18 19 20"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
-!!! tip
- The `openapi_prefix` will contain any prefix needed for the generated OpenAPI *path operations*.
-
- FastAPI will automatically use the `root_path` to pass it in the `openapi_prefix`.
-
- But the important thing is that your function should receive that parameter `openapi_prefix` and pass it along.
-
### Modify the OpenAPI schema
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
-```Python hl_lines="22 23 24"
+```Python hl_lines="21 22 23"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
@@ -79,7 +71,7 @@ That way, your application won't have to generate the schema every time a user o
It will be generated only once, and then the same cached schema will be used for the next requests.
-```Python hl_lines="13 14 25 26"
+```Python hl_lines="13 14 24 25"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
@@ -87,7 +79,7 @@ It will be generated only once, and then the same cached schema will be used for
Now you can replace the `.openapi()` method with your new function.
-```Python hl_lines="29"
+```Python hl_lines="28"
{!../../../docs_src/extending_openapi/tutorial001.py!}
```
diff --git a/docs/en/docs/img/tutorial/behind-a-proxy/image01.png b/docs/en/docs/img/tutorial/behind-a-proxy/image01.png
index 4ceae44219..8012031401 100644
Binary files a/docs/en/docs/img/tutorial/behind-a-proxy/image01.png and b/docs/en/docs/img/tutorial/behind-a-proxy/image01.png differ
diff --git a/docs/en/docs/img/tutorial/behind-a-proxy/image02.png b/docs/en/docs/img/tutorial/behind-a-proxy/image02.png
index 8012031401..95c207fcf8 100644
Binary files a/docs/en/docs/img/tutorial/behind-a-proxy/image02.png and b/docs/en/docs/img/tutorial/behind-a-proxy/image02.png differ
diff --git a/docs/en/docs/img/tutorial/behind-a-proxy/image03.png b/docs/en/docs/img/tutorial/behind-a-proxy/image03.png
new file mode 100644
index 0000000000..278bd07c8e
Binary files /dev/null and b/docs/en/docs/img/tutorial/behind-a-proxy/image03.png differ
diff --git a/docs/en/docs/img/tutorial/security/image02.png b/docs/en/docs/img/tutorial/security/image02.png
index f434335a6b..a437ac0e71 100644
Binary files a/docs/en/docs/img/tutorial/security/image02.png and b/docs/en/docs/img/tutorial/security/image02.png differ
diff --git a/docs/en/docs/img/tutorial/security/image04.png b/docs/en/docs/img/tutorial/security/image04.png
index d56ed53e4c..231c53d2ef 100644
Binary files a/docs/en/docs/img/tutorial/security/image04.png and b/docs/en/docs/img/tutorial/security/image04.png differ
diff --git a/docs/en/docs/img/tutorial/security/image05.png b/docs/en/docs/img/tutorial/security/image05.png
index af2023a183..cd4525f571 100644
Binary files a/docs/en/docs/img/tutorial/security/image05.png and b/docs/en/docs/img/tutorial/security/image05.png differ
diff --git a/docs/en/docs/img/tutorial/security/image08.png b/docs/en/docs/img/tutorial/security/image08.png
index 64cc76293c..5289afce28 100644
Binary files a/docs/en/docs/img/tutorial/security/image08.png and b/docs/en/docs/img/tutorial/security/image08.png differ
diff --git a/docs/en/docs/img/tutorial/security/image11.png b/docs/en/docs/img/tutorial/security/image11.png
index 3e1a0ce919..278f049400 100644
Binary files a/docs/en/docs/img/tutorial/security/image11.png and b/docs/en/docs/img/tutorial/security/image11.png differ
diff --git a/docs/en/docs/tutorial/security/first-steps.md b/docs/en/docs/tutorial/security/first-steps.md
index 287e92c23e..81226dffae 100644
--- a/docs/en/docs/tutorial/security/first-steps.md
+++ b/docs/en/docs/tutorial/security/first-steps.md
@@ -86,8 +86,8 @@ But in this case, the same **FastAPI** application will handle the API and the a
So, let's review it from that simplified point of view:
* The user types his `username` and `password` in the frontend, and hits `Enter`.
-* The frontend (running in the user's browser) sends that `username` and `password` to a specific URL in our API.
-* The API checks that `username` and `password`, and responds with a "token".
+* The frontend (running in the user's browser) sends that `username` and `password` to a specific URL in our API (declared with `tokenUrl="token"`).
+* The API checks that `username` and `password`, and responds with a "token" (we haven't implemented any of this yet).
* A "token" is just a string with some content that we can use later to verify this user.
* Normally, a token is set to expire after some time.
* So, the user will have to login again at some point later.
@@ -114,13 +114,20 @@ In this example we are going to use **OAuth2**, with the **Password** flow, usin
In that case, **FastAPI** also provides you with the tools to build it.
-`OAuth2PasswordBearer` is a class that we create passing a parameter of the URL in where the client (the frontend running in the user's browser) can use to send the `username` and `password` and get a token.
+`OAuth2PasswordBearer` is a class that we create passing a parameter with the URL the client (the frontend running in the user's browser) can use to send the `username` and `password` and get a token.
```Python hl_lines="6"
{!../../../docs_src/security/tutorial001.py!}
```
-It doesn't create that endpoint / *path operation*, but declares that that URL is the one that the client should use to get the token. That information is used in OpenAPI, and then in the interactive API documentation systems.
+!!! tip
+ here `tokenUrl="token"` refers to a relative URL `token` that we haven't created yet. As it's a relative URL, it's equivalent to `./token`.
+
+ Because we are using a relative URL, if your API was located at `https://example.com/`, then it would refer to `https://example.com/token`. But if your API was located at `https://example.com/api/v1/`, then it would refer to `https://example.com/api/v1/token`.
+
+ Using a relative URL is important to make sure your application keeps working even in an advanced use case like [Behind a Proxy](../../advanced/behind-a-proxy.md){.internal-link target=_blank}.
+
+It doesn't create that endpoint / *path operation* for `./token`, but declares that that URL `./token` is the one that the client should use to get the token. That information is used in OpenAPI, and then in the interactive API documentation systems.
!!! info
If you are a very strict "Pythonista" you might dislike the style of the parameter name `tokenUrl` instead of `token_url`.
diff --git a/docs/en/docs/tutorial/security/simple-oauth2.md b/docs/en/docs/tutorial/security/simple-oauth2.md
index 2f58a6d109..d3343c3e88 100644
--- a/docs/en/docs/tutorial/security/simple-oauth2.md
+++ b/docs/en/docs/tutorial/security/simple-oauth2.md
@@ -47,7 +47,7 @@ Now let's use the utilities provided by **FastAPI** to handle this.
### `OAuth2PasswordRequestForm`
-First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` for the path `/token`:
+First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`:
```Python hl_lines="4 76"
{!../../../docs_src/security/tutorial003.py!}
diff --git a/docs_src/behind_a_proxy/tutorial003.py b/docs_src/behind_a_proxy/tutorial003.py
new file mode 100644
index 0000000000..3b7d8be018
--- /dev/null
+++ b/docs_src/behind_a_proxy/tutorial003.py
@@ -0,0 +1,14 @@
+from fastapi import FastAPI, Request
+
+app = FastAPI(
+ servers=[
+ {"url": "https://stag.example.com", "description": "Staging environment"},
+ {"url": "https://prod.example.com", "description": "Production environment"},
+ ],
+ root_path="/api/v1",
+)
+
+
+@app.get("/app")
+def read_main(request: Request):
+ return {"message": "Hello World", "root_path": request.scope.get("root_path")}
diff --git a/docs_src/behind_a_proxy/tutorial004.py b/docs_src/behind_a_proxy/tutorial004.py
new file mode 100644
index 0000000000..51bd5babc1
--- /dev/null
+++ b/docs_src/behind_a_proxy/tutorial004.py
@@ -0,0 +1,15 @@
+from fastapi import FastAPI, Request
+
+app = FastAPI(
+ servers=[
+ {"url": "https://stag.example.com", "description": "Staging environment"},
+ {"url": "https://prod.example.com", "description": "Production environment"},
+ ],
+ root_path="/api/v1",
+ root_path_in_servers=False,
+)
+
+
+@app.get("/app")
+def read_main(request: Request):
+ return {"message": "Hello World", "root_path": request.scope.get("root_path")}
diff --git a/docs_src/extending_openapi/tutorial001.py b/docs_src/extending_openapi/tutorial001.py
index d9d7e9844b..561e95898f 100644
--- a/docs_src/extending_openapi/tutorial001.py
+++ b/docs_src/extending_openapi/tutorial001.py
@@ -9,7 +9,7 @@ async def read_items():
return [{"name": "Foo"}]
-def custom_openapi(openapi_prefix: str):
+def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
@@ -17,7 +17,6 @@ def custom_openapi(openapi_prefix: str):
version="2.5.0",
description="This is a very custom OpenAPI schema",
routes=app.routes,
- openapi_prefix=openapi_prefix,
)
openapi_schema["info"]["x-logo"] = {
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png"
diff --git a/docs_src/security/tutorial001.py b/docs_src/security/tutorial001.py
index bd8ce4d101..224e59602e 100644
--- a/docs_src/security/tutorial001.py
+++ b/docs_src/security/tutorial001.py
@@ -3,7 +3,7 @@ from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
diff --git a/docs_src/security/tutorial002.py b/docs_src/security/tutorial002.py
index 2fccec7ed2..03e0cd5fce 100644
--- a/docs_src/security/tutorial002.py
+++ b/docs_src/security/tutorial002.py
@@ -6,7 +6,7 @@ from pydantic import BaseModel
app = FastAPI()
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class User(BaseModel):
diff --git a/docs_src/security/tutorial003.py b/docs_src/security/tutorial003.py
index 0fd15ae3c7..a6bb176e4f 100644
--- a/docs_src/security/tutorial003.py
+++ b/docs_src/security/tutorial003.py
@@ -28,7 +28,7 @@ def fake_hash_password(password: str):
return "fakehashed" + password
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
class User(BaseModel):
diff --git a/docs_src/security/tutorial004.py b/docs_src/security/tutorial004.py
index f3cb6a905b..54dbe46d7e 100644
--- a/docs_src/security/tutorial004.py
+++ b/docs_src/security/tutorial004.py
@@ -48,7 +48,7 @@ class UserInDB(User):
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
-oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
diff --git a/docs_src/security/tutorial005.py b/docs_src/security/tutorial005.py
index 05c29deb19..d66acb03cc 100644
--- a/docs_src/security/tutorial005.py
+++ b/docs_src/security/tutorial005.py
@@ -61,7 +61,7 @@ class UserInDB(User):
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(
- tokenUrl="/token",
+ tokenUrl="token",
scopes={"me": "Read information about the current user.", "items": "Read items."},
)
diff --git a/fastapi/applications.py b/fastapi/applications.py
index 08dba9b599..d5b70e116f 100644
--- a/fastapi/applications.py
+++ b/fastapi/applications.py
@@ -50,6 +50,7 @@ class FastAPI(Starlette):
on_shutdown: Sequence[Callable] = None,
openapi_prefix: str = "",
root_path: str = "",
+ root_path_in_servers: bool = True,
**extra: Dict[str, Any],
) -> None:
self.default_response_class = default_response_class
@@ -71,7 +72,7 @@ class FastAPI(Starlette):
self.title = title
self.description = description
self.version = version
- self.servers = servers
+ self.servers = servers or []
self.openapi_url = openapi_url
self.openapi_tags = openapi_tags
# TODO: remove when discarding the openapi_prefix parameter
@@ -83,6 +84,7 @@ class FastAPI(Starlette):
"https://fastapi.tiangolo.com/advanced/sub-applications/"
)
self.root_path = root_path or openapi_prefix
+ self.root_path_in_servers = root_path_in_servers
self.docs_url = docs_url
self.redoc_url = redoc_url
self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url
@@ -98,7 +100,7 @@ class FastAPI(Starlette):
self.openapi_schema: Optional[Dict[str, Any]] = None
self.setup()
- def openapi(self, openapi_prefix: str = "") -> Dict:
+ def openapi(self) -> Dict:
if not self.openapi_schema:
self.openapi_schema = get_openapi(
title=self.title,
@@ -106,7 +108,6 @@ class FastAPI(Starlette):
openapi_version=self.openapi_version,
description=self.description,
routes=self.routes,
- openapi_prefix=openapi_prefix,
tags=self.openapi_tags,
servers=self.servers,
)
@@ -114,10 +115,19 @@ class FastAPI(Starlette):
def setup(self) -> None:
if self.openapi_url:
+ server_urls = set()
+ for server_data in self.servers:
+ url = server_data.get("url")
+ if url:
+ server_urls.add(url)
async def openapi(req: Request) -> JSONResponse:
root_path = req.scope.get("root_path", "").rstrip("/")
- return JSONResponse(self.openapi(root_path))
+ if root_path not in server_urls:
+ if root_path and self.root_path_in_servers:
+ self.servers.insert(0, {"url": root_path})
+ server_urls.add(root_path)
+ return JSONResponse(self.openapi())
self.add_route(self.openapi_url, openapi, include_in_schema=False)
if self.openapi_url and self.docs_url:
diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py
index ad1a9d83bb..1cf79d71e3 100644
--- a/fastapi/openapi/utils.py
+++ b/fastapi/openapi/utils.py
@@ -331,7 +331,6 @@ def get_openapi(
openapi_version: str = "3.0.2",
description: str = None,
routes: Sequence[BaseRoute],
- openapi_prefix: str = "",
tags: Optional[List[Dict[str, Any]]] = None,
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
) -> Dict:
@@ -356,9 +355,7 @@ def get_openapi(
if result:
path, security_schemes, path_definitions = result
if path:
- paths.setdefault(openapi_prefix + route.path_format, {}).update(
- path
- )
+ paths.setdefault(route.path_format, {}).update(path)
if security_schemes:
components.setdefault("securitySchemes", {}).update(
security_schemes
diff --git a/pending_tests/main.py b/pending_tests/main.py
index cb464cb312..5e919f1bcf 100644
--- a/pending_tests/main.py
+++ b/pending_tests/main.py
@@ -31,7 +31,7 @@ def get_security(sec=Security(HTTPBasic())):
reusable_oauth2 = OAuth2(
flows={
"password": {
- "tokenUrl": "/token",
+ "tokenUrl": "token",
"scopes": {"read:user": "Read a User", "write:user": "Create a user"},
}
}
diff --git a/tests/test_deprecated_openapi_prefix.py b/tests/test_deprecated_openapi_prefix.py
index df7e69bd56..a3355256f9 100644
--- a/tests/test_deprecated_openapi_prefix.py
+++ b/tests/test_deprecated_openapi_prefix.py
@@ -15,7 +15,7 @@ openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
- "/api/v1/app": {
+ "/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
@@ -28,6 +28,7 @@ openapi_schema = {
}
}
},
+ "servers": [{"url": "/api/v1"}],
}
diff --git a/tests/test_security_oauth2.py b/tests/test_security_oauth2.py
index 6c513039f2..a0ae93afa6 100644
--- a/tests/test_security_oauth2.py
+++ b/tests/test_security_oauth2.py
@@ -9,7 +9,7 @@ app = FastAPI()
reusable_oauth2 = OAuth2(
flows={
"password": {
- "tokenUrl": "/token",
+ "tokenUrl": "token",
"scopes": {"read:users": "Read the users", "write:users": "Create users"},
}
}
@@ -144,7 +144,7 @@ openapi_schema = {
"read:users": "Read the users",
"write:users": "Create users",
},
- "tokenUrl": "/token",
+ "tokenUrl": "token",
}
},
}
diff --git a/tests/test_security_oauth2_authorization_code_bearer.py b/tests/test_security_oauth2_authorization_code_bearer.py
index 3e0155e1fa..ad9a39ded7 100644
--- a/tests/test_security_oauth2_authorization_code_bearer.py
+++ b/tests/test_security_oauth2_authorization_code_bearer.py
@@ -7,7 +7,7 @@ from fastapi.testclient import TestClient
app = FastAPI()
oauth2_scheme = OAuth2AuthorizationCodeBearer(
- authorizationUrl="/authorize", tokenUrl="/token", auto_error=True
+ authorizationUrl="authorize", tokenUrl="token", auto_error=True
)
@@ -42,8 +42,8 @@ openapi_schema = {
"type": "oauth2",
"flows": {
"authorizationCode": {
- "authorizationUrl": "/authorize",
- "tokenUrl": "/token",
+ "authorizationUrl": "authorize",
+ "tokenUrl": "token",
"scopes": {},
}
},
diff --git a/tests/test_security_oauth2_optional.py b/tests/test_security_oauth2_optional.py
index c2c9764b05..e9e6d5d27a 100644
--- a/tests/test_security_oauth2_optional.py
+++ b/tests/test_security_oauth2_optional.py
@@ -11,7 +11,7 @@ app = FastAPI()
reusable_oauth2 = OAuth2(
flows={
"password": {
- "tokenUrl": "/token",
+ "tokenUrl": "token",
"scopes": {"read:users": "Read the users", "write:users": "Create users"},
}
},
@@ -148,7 +148,7 @@ openapi_schema = {
"read:users": "Read the users",
"write:users": "Create users",
},
- "tokenUrl": "/token",
+ "tokenUrl": "token",
}
},
}
diff --git a/tests/test_tutorial/test_behind_a_proxy/test_tutorial001.py b/tests/test_tutorial/test_behind_a_proxy/test_tutorial001.py
index a6b7401cf8..be9e499bf8 100644
--- a/tests/test_tutorial/test_behind_a_proxy/test_tutorial001.py
+++ b/tests/test_tutorial/test_behind_a_proxy/test_tutorial001.py
@@ -8,7 +8,7 @@ openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
- "/api/v1/app": {
+ "/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
@@ -21,6 +21,7 @@ openapi_schema = {
}
}
},
+ "servers": [{"url": "/api/v1"}],
}
diff --git a/tests/test_tutorial/test_behind_a_proxy/test_tutorial002.py b/tests/test_tutorial/test_behind_a_proxy/test_tutorial002.py
index 26da7dea46..ac192e3db7 100644
--- a/tests/test_tutorial/test_behind_a_proxy/test_tutorial002.py
+++ b/tests/test_tutorial/test_behind_a_proxy/test_tutorial002.py
@@ -8,7 +8,7 @@ openapi_schema = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
- "/api/v1/app": {
+ "/app": {
"get": {
"summary": "Read Main",
"operationId": "read_main_app_get",
@@ -21,6 +21,7 @@ openapi_schema = {
}
}
},
+ "servers": [{"url": "/api/v1"}],
}
diff --git a/tests/test_tutorial/test_behind_a_proxy/test_tutorial003.py b/tests/test_tutorial/test_behind_a_proxy/test_tutorial003.py
new file mode 100644
index 0000000000..2727525ca4
--- /dev/null
+++ b/tests/test_tutorial/test_behind_a_proxy/test_tutorial003.py
@@ -0,0 +1,41 @@
+from fastapi.testclient import TestClient
+
+from docs_src.behind_a_proxy.tutorial003 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "servers": [
+ {"url": "/api/v1"},
+ {"url": "https://stag.example.com", "description": "Staging environment"},
+ {"url": "https://prod.example.com", "description": "Production environment"},
+ ],
+ "paths": {
+ "/app": {
+ "get": {
+ "summary": "Read Main",
+ "operationId": "read_main_app_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ }
+ }
+ },
+}
+
+
+def test_openapi():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == openapi_schema
+
+
+def test_main():
+ response = client.get("/app")
+ assert response.status_code == 200
+ assert response.json() == {"message": "Hello World", "root_path": "/api/v1"}
diff --git a/tests/test_tutorial/test_behind_a_proxy/test_tutorial004.py b/tests/test_tutorial/test_behind_a_proxy/test_tutorial004.py
new file mode 100644
index 0000000000..4c4e4b75c2
--- /dev/null
+++ b/tests/test_tutorial/test_behind_a_proxy/test_tutorial004.py
@@ -0,0 +1,40 @@
+from fastapi.testclient import TestClient
+
+from docs_src.behind_a_proxy.tutorial004 import app
+
+client = TestClient(app)
+
+openapi_schema = {
+ "openapi": "3.0.2",
+ "info": {"title": "FastAPI", "version": "0.1.0"},
+ "servers": [
+ {"url": "https://stag.example.com", "description": "Staging environment"},
+ {"url": "https://prod.example.com", "description": "Production environment"},
+ ],
+ "paths": {
+ "/app": {
+ "get": {
+ "summary": "Read Main",
+ "operationId": "read_main_app_get",
+ "responses": {
+ "200": {
+ "description": "Successful Response",
+ "content": {"application/json": {"schema": {}}},
+ }
+ },
+ }
+ }
+ },
+}
+
+
+def test_openapi():
+ response = client.get("/openapi.json")
+ assert response.status_code == 200
+ assert response.json() == openapi_schema
+
+
+def test_main():
+ response = client.get("/app")
+ assert response.status_code == 200
+ assert response.json() == {"message": "Hello World", "root_path": "/api/v1"}
diff --git a/tests/test_tutorial/test_security/test_tutorial001.py b/tests/test_tutorial/test_security/test_tutorial001.py
index 82818b430b..8a033c4f26 100644
--- a/tests/test_tutorial/test_security/test_tutorial001.py
+++ b/tests/test_tutorial/test_security/test_tutorial001.py
@@ -26,7 +26,7 @@ openapi_schema = {
"securitySchemes": {
"OAuth2PasswordBearer": {
"type": "oauth2",
- "flows": {"password": {"scopes": {}, "tokenUrl": "/token"}},
+ "flows": {"password": {"scopes": {}, "tokenUrl": "token"}},
}
}
},
diff --git a/tests/test_tutorial/test_security/test_tutorial003.py b/tests/test_tutorial/test_security/test_tutorial003.py
index bf2a81723b..3fc7f5f40f 100644
--- a/tests/test_tutorial/test_security/test_tutorial003.py
+++ b/tests/test_tutorial/test_security/test_tutorial003.py
@@ -102,7 +102,7 @@ openapi_schema = {
"securitySchemes": {
"OAuth2PasswordBearer": {
"type": "oauth2",
- "flows": {"password": {"scopes": {}, "tokenUrl": "/token"}},
+ "flows": {"password": {"scopes": {}, "tokenUrl": "token"}},
}
},
},
diff --git a/tests/test_tutorial/test_security/test_tutorial005.py b/tests/test_tutorial/test_security/test_tutorial005.py
index 509b200da2..a37f2d60ac 100644
--- a/tests/test_tutorial/test_security/test_tutorial005.py
+++ b/tests/test_tutorial/test_security/test_tutorial005.py
@@ -168,7 +168,7 @@ openapi_schema = {
"me": "Read information about the current user.",
"items": "Read items.",
},
- "tokenUrl": "/token",
+ "tokenUrl": "token",
}
},
}
diff --git a/tests/test_tutorial/test_sub_applications/test_tutorial001.py b/tests/test_tutorial/test_sub_applications/test_tutorial001.py
index 4e1dc9e099..00e9aec577 100644
--- a/tests/test_tutorial/test_sub_applications/test_tutorial001.py
+++ b/tests/test_tutorial/test_sub_applications/test_tutorial001.py
@@ -26,7 +26,7 @@ openapi_schema_sub = {
"openapi": "3.0.2",
"info": {"title": "FastAPI", "version": "0.1.0"},
"paths": {
- "/subapi/sub": {
+ "/sub": {
"get": {
"responses": {
"200": {
@@ -39,6 +39,7 @@ openapi_schema_sub = {
}
}
},
+ "servers": [{"url": "/subapi"}],
}