โ ๐ณ¶
โ , ๐ 5๏ธโฃ๐ ๐ช โ๏ธ ๐ณ ๐ฝ ๐ Traefik โ๏ธ ๐ โฎ๏ธ ๐ณ ๐ ๐ฎ โ โก ๐ก ๐ ๐ซ ๐ ๐ ๐ธ.
๐ซ ๐ผ ๐ ๐ช โ๏ธ root_path ๐ ๐ ๐ธ.
root_path ๐ ๏ธ ๐ ๐ซ ๐ง (๐ FastAPI ๐ ๐, ๐ ๐).
root_path โ๏ธ ๐ต ๐ซ ๐ฏ ๐ผ.
& โซ๏ธ โ๏ธ ๐ ๐โ ๐ ๐ง-๐ธ.
๐ณ โฎ๏ธ ๐ โก ๐ก¶
โ๏ธ ๐ณ โฎ๏ธ ๐ โก ๐ก, ๐ ๐ผ, โ ๐ ๐ ๐ช ๐ฃ โก /app ๐ ๐, โ๏ธ โคด๏ธ, ๐ ๐ฎ ๐งฝ ๐ ๐ (๐ณ) ๐ ๐ ๐ฎ ๐ FastAPI ๐ธ ๐ฝ โก ๐ /api/v1.
๐ ๐ผ, โฎ๏ธ โก /app ๐ ๐ค ๐ฆ /api/v1/app.
โ๏ธ ๐ ๐ ๐ โ ๐ค ๐ค /app.
& ๐ณ ๐ "โ" โก ๐ก ๐ โ โญ ๐ถ ๐จ Uvicorn, ๐ง ๐ ๐ธ ๐ค ๐ โซ๏ธ ๐ฆ /app, ๐ ๐ ๐ซ โ๏ธ โน ๐ ๐ ๐ ๐ ๐ก /api/v1.
๐ ๐ฅ, ๐ ๐ ๐ท ๐.
โ๏ธ โคด๏ธ, ๐โ ๐ ๐ ๐ ๏ธ ๐ฉบ ๐ (๐ธ), โซ๏ธ ๐ โ ๐ค ๐ ๐ /openapi.json, โฉ๏ธ /api/v1/openapi.json.
, ๐ธ (๐ ๐ ๐ฅ) ๐ ๐ ๐ /openapi.json & ๐ซ๐ ๐ช ๐ค ๐ ๐.
โฉ๏ธ ๐ฅ โ๏ธ ๐ณ โฎ๏ธ โก ๐ก /api/v1 ๐ ๐ฑ, ๐ธ ๐ช โ ๐ ๐ /api/v1/openapi.json.
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
Tip
๐ข 0.0.0.0 ๐ โ๏ธ โ ๐ ๐ ๐ ๐ ๐ ๐ข ๐ช ๐ ๐ฐ/๐ฝ.
๐ฉบ ๐ ๐ ๐ช ๐ ๐ ๐ฃ ๐ ๐ ๐ ๏ธ server ๐ /api/v1 (โ
๐ณ). ๐ผ:
{
"openapi": "3.0.2",
// More stuff here
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// More stuff here
}
}
๐ ๐ผ, "๐ณ" ๐ช ๐ณ ๐ Traefik. & ๐ฝ ๐ ๐ณ ๐ Uvicorn, ๐โโ ๐ FastAPI ๐ธ.
๐ root_path¶
๐ ๐, ๐ ๐ช โ๏ธ ๐ โธ ๐ --root-path ๐:
$ uvicorn main:app --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
๐ฅ ๐ โ๏ธ Hypercorn, โซ๏ธ โ๏ธ ๐ --root-path.
๐ก โน
๐ซ ๐ง ๐ฌ root_path ๐ โ๏ธ ๐ผ.
& --root-path ๐ โธ ๐ ๐ ๐ root_path.
โ
โฎ๏ธ root_path¶
๐ ๐ช ๐ค โฎ๏ธ root_path โ๏ธ ๐ ๐ธ ๐ ๐จ, โซ๏ธ ๐ scope ๐ (๐ ๐ ๐ซ ๐).
๐ฅ ๐ฅ โ โซ๏ธ ๐ง ๐ฆ ๐ฏ.
from fastapi import FastAPI, Request
app = FastAPI()
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
โคด๏ธ, ๐ฅ ๐ โถ๏ธ Uvicorn โฎ๏ธ:
$ uvicorn main:app --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
๐จ ๐ ๐ณ ๐:
{
"message": "Hello World",
"root_path": "/api/v1"
}
โ root_path FastAPI ๐ฑ¶
๐, ๐ฅ ๐ ๐ซ โ๏ธ ๐ ๐ ๐ โธ ๐ ๐ --root-path โ๏ธ ๐, ๐ ๐ช โ root_path ๐ข ๐โ ๐ ๐ FastAPI ๐ฑ:
from fastapi import FastAPI, Request
app = FastAPI(root_path="/api/v1")
@app.get("/app")
def read_main(request: Request):
return {"message": "Hello World", "root_path": request.scope.get("root_path")}
๐ถโโ๏ธ root_path FastAPI ๐ ๐ ๐ถโโ๏ธ --root-path ๐ โธ ๐ Uvicorn โ๏ธ Hypercorn.
๐ root_path¶
โ๏ธ ๐คฏ ๐ ๐ฝ (Uvicorn) ๐ ๐ซ โ๏ธ ๐ root_path ๐ณ ๐ ๐ ๐ถโโ๏ธ โซ๏ธ ๐ฑ.
โ๏ธ ๐ฅ ๐ ๐ถ โฎ๏ธ ๐ ๐ฅ http://127.0.0.1:8000/app ๐ ๐ ๐ ๐ ๐จ:
{
"message": "Hello World",
"root_path": "/api/v1"
}
, โซ๏ธ ๐ ๐ซ โ ๐ http://127.0.0.1:8000/api/v1/app.
Uvicorn ๐ โ ๐ณ ๐ Uvicorn http://127.0.0.1:8000/app, & โคด๏ธ โซ๏ธ ๐ ๐ณ ๐ฏ ๐ฎ โ /api/v1 ๐ก ๐ ๐.
๐ ๐ณ โฎ๏ธ ๐ โก ๐ก¶
โ๏ธ ๐คฏ ๐ ๐ณ โฎ๏ธ ๐ โก ๐ก ๐ด 1๏ธโฃ ๐ ๐ โซ๏ธ.
๐ฒ ๐ ๐ผ ๐ข ๐ ๐ ๐ณ ๐ซ โ๏ธ ๐ โก ๐ก.
๐ผ ๐ ๐ (๐ต ๐ โก ๐ก), ๐ณ ๐ ๐ ๐ ๐ณ ๐ https://myawesomeapp.com, & โคด๏ธ ๐ฅ ๐ฅ ๐ถ https://myawesomeapp.com/api/v1/app & ๐ ๐ฝ (โ
Uvicorn) ๐ ๐ http://127.0.0.1:8000 ๐ณ (๐ต ๐ โก ๐ก) ๐ ๐ Uvicorn ๐ โก: http://127.0.0.1:8000/api/v1/app.
๐ฌ ๐ โฎ๏ธ Traefik¶
๐ ๐ช ๐ช ๐ ๐ฅผ ๐ โฎ๏ธ ๐ โก ๐ก โ๏ธ Traefik.
โฌ Traefik, โซ๏ธ ๐ ๐ฑ, ๐ ๐ช โ ๐ ๐ & ๐ โซ๏ธ ๐ โช๏ธโก๏ธ ๐ถ.
โคด๏ธ โ ๐ traefik.toml โฎ๏ธ:
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
๐ ๐ฌ Traefik ๐ ๐ โด 9๏ธโฃ9๏ธโฃ9๏ธโฃ9๏ธโฃ & โ๏ธ โ1๏ธโฃ ๐ routes.toml.
Tip
๐ฅ โ๏ธ โด 9๏ธโฃ9๏ธโฃ9๏ธโฃ9๏ธโฃ โฉ๏ธ ๐ฉ ๐บ๐ธ๐ โด 8๏ธโฃ0๏ธโฃ ๐ ๐ ๐ซ โ๏ธ ๐ โซ๏ธ โฎ๏ธ ๐ก (sudo) ๐.
๐ โ ๐ ๐ ๐ routes.toml:
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
๐ ๐ ๐ Traefik โ๏ธ โก ๐ก /api/v1.
& โคด๏ธ โซ๏ธ ๐ โ ๐ฎ ๐จ ๐ Uvicorn ๐โโ ๐ http://127.0.0.1:8000.
๐ โถ๏ธ Traefik:
$ ./traefik --configFile=traefik.toml
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
& ๐ โถ๏ธ ๐ ๐ฑ โฎ๏ธ Uvicorn, โ๏ธ --root-path ๐:
$ uvicorn main:app --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
โ ๐จ¶
๐, ๐ฅ ๐ ๐ถ ๐ โฎ๏ธ โด Uvicorn: http://127.0.0.1:8000/app, ๐ ๐ ๐ ๐ ๐จ:
{
"message": "Hello World",
"root_path": "/api/v1"
}
Tip
๐ ๐ โ๏ธ ๐ ๐ โซ๏ธ http://127.0.0.1:8000/app โซ๏ธ ๐ฆ root_path /api/v1, โ โช๏ธโก๏ธ ๐ --root-path.
& ๐ ๐ ๐ โฎ๏ธ โด Traefik, โ โก ๐ก: http://127.0.0.1:9999/api/v1/app.
๐ฅ ๐ค ๐ ๐จ:
{
"message": "Hello World",
"root_path": "/api/v1"
}
โ๏ธ ๐ ๐ฐ ๐ โฎ๏ธ ๐ก โก ๐ ๐ณ: /api/v1.
โ๏ธ, ๐ญ ๐ฅ ๐ ๐ฑ ๐ ๐ ๐ฑ ๐ ๐ณ, โฌ โฎ๏ธ โก ๐ก /app/v1 "โ" 1๏ธโฃ.
& โฌ ๐ต โก ๐ก (http://127.0.0.1:8000/app), ๐ Uvicorn ๐, ๐ ๐ฏ ๐ณ (Traefik) ๐ โซ๏ธ.
๐ ๐ฆ โ ๐ณ (Traefik) โ๏ธ โก ๐ก & โ ๐ฝ (Uvicorn) โ๏ธ root_path โช๏ธโก๏ธ ๐ --root-path.
โ ๐ฉบ ๐¶
โ๏ธ ๐ฅ ๐ ๐. ๐ถ
"๐" ๐ ๐ ๐ฑ ๐ ๐ ๐ณ โฎ๏ธ โก ๐ก ๐ ๐ฅ ๐ฌ. , ๐ฅ ๐ โ, ๐ฅ ๐ ๐ ๐ฉบ ๐ ๐ฆ Uvicorn ๐, ๐ต โก ๐ก ๐, โซ๏ธ ๐ ๐ซ ๐ท, โฉ๏ธ โซ๏ธ โ ๐ ๐ ๐ณ.
๐ ๐ช โ โซ๏ธ http://127.0.0.1:8000/docs:

โ๏ธ ๐ฅ ๐ฅ ๐ ๐ฉบ ๐ "๐" ๐ โ๏ธ ๐ณ โฎ๏ธ โด 9999, /api/v1/docs, โซ๏ธ ๐ท โ โ ๐ถ
๐ ๐ช โ โซ๏ธ http://127.0.0.1:9999/api/v1/docs:

โถ๏ธ๏ธ ๐ฅ ๐ โซ๏ธ. ๐ถ ๐ถ
๐ โฉ๏ธ FastAPI โ๏ธ ๐ root_path โ ๐ข server ๐ โฎ๏ธ ๐ ๐ root_path.
๐ ๐ฝ¶
Warning
๐ ๐ ๐ง โ๏ธ ๐ผ. ๐ญ ๐ ๐ถ โซ๏ธ.
๐ข, FastAPI ๐ โ server ๐ ๐ โฎ๏ธ ๐ root_path.
โ๏ธ ๐ ๐ช ๐ ๐ ๐ servers, ๐ผ ๐ฅ ๐ ๐ ๐ ๐ฉบ ๐ ๐ โฎ๏ธ ๐ & ๐ญ ๐.
๐ฅ ๐ ๐ถโโ๏ธ ๐ ๐ servers & ๐ค root_path (โฉ๏ธ ๐ ๐ ๏ธ ๐จโโคโ๐จ โ
๐ณ), FastAPI ๐ ๐ฉ "๐ฝ" โฎ๏ธ ๐ root_path โถ๏ธ ๐.
๐ผ:
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")}
๐ ๐ ๐ ๐ ๐:
{
"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
๐ ๐-๐ ๐ฝ โฎ๏ธ url ๐ฒ /api/v1, โ โช๏ธโก๏ธ root_path.
๐ฉบ ๐ http://127.0.0.1:9999/api/v1/docs โซ๏ธ ๐ ๐ ๐:

Tip
๐ฉบ ๐ ๐ ๐ โฎ๏ธ ๐ฝ ๐ ๐ ๐.
โ ๐ง ๐ฝ โช๏ธโก๏ธ root_path¶
๐ฅ ๐ ๐ซ ๐ FastAPI ๐ ๐ง ๐ฝ โ๏ธ root_path, ๐ ๐ช โ๏ธ ๐ข root_path_in_servers=False:
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")}
& โคด๏ธ โซ๏ธ ๐ ๐ซ ๐ โซ๏ธ ๐ ๐.
๐ ๐ง-๐ธ¶
๐ฅ ๐ ๐ช ๐ป ๐ง-๐ธ (๐ฌ ๐ง ๐ธ - ๐ป) โช โ๏ธ ๐ณ โฎ๏ธ root_path, ๐ ๐ช โซ๏ธ ๐, ๐ ๐ โ.
FastAPI ๐ ๐ โ๏ธ root_path ๐, โซ๏ธ ๐ ๐ท. ๐ถ