FastAPI: Retrieve URL from view name ( route name )
Suppose I have following views,
from fastapi import FastAPI
app = FastAPI()
@app.get('/hello/')
def hello_world():
return {"msg": "Hello World"}
@app.get('/hello/{number}/')
def hello_world_number(number: int):
return {"msg": "Hello World Number", "number": number}
I have been using these functions in Flask and Django
- Flask:
url_for(...)
- Django:
reverse(...)
So, how can I obtain/build the URLs of hello_world
and hello_world_number
in a similar way?
Solution 1:
We have got Router.url_path_for(...)
method which is located inside the starlette package
Method-1: Using FastAPI
instance
This method is useful when you are able to access the FastAPI
instance in your current context. (Thanks to @Yagizcan Degirmenci)
from fastapi import FastAPI
app = FastAPI()
@app.get('/hello/')
def hello_world():
return {"msg": "Hello World"}
@app.get('/hello/{number}/')
def hello_world_number(number: int):
return {"msg": "Hello World Number", "number": number}
print(app.url_path_for('hello_world'))
print(app.url_path_for('hello_world_number', number=1}))
print(app.url_path_for('hello_world_number', number=2}))
# Results
/hello/
/hello/1/
/hello/2/
Drawback
- If we are using
APIRouter
,router.url_path_for('hello_world')
may not work sincerouter
isn't an instance ofFastAPI
class. That is, we must have theFastAPI
instance to resolve the URL
Method-2: Request
instance
This method is useful when you are able to access the Request
instance (the incoming request), usually, within a view.
from fastapi import FastAPI, Request
app = FastAPI()
@app.get('/hello/')
def hello_world():
return {"msg": "Hello World"}
@app.get('/hello/{number}/')
def hello_world_number(number: int):
return {"msg": "Hello World Number", "number": number}
@app.get('/')
def named_url_reveres(request: Request):
return {
"URL for 'hello_world'": request.url_for("hello_world"),
"URL for 'hello_world_number' with number '1'": request.url_for("hello_world_number", number=1),
"URL for 'hello_world_number' with number '2''": request.url_for("hello_world_number", number=2})
}
# Result Response
{
"URL for 'hello_world'": "http://0.0.0.0:6022/hello/",
"URL for 'hello_world_number' with number '1'": "http://0.0.0.0:6022/hello/1/",
"URL for 'hello_world_number' with number '2''": "http://0.0.0.0:6022/hello/2/"
}
Drawback
- We must include the
request
parameter in every (or required) view to resolve the URL, which might raise an ugly feel to developers.
Solution 2:
Actually you don't need to reinvent the wheel. FastAPI supports this out-of-box (Actually Starlette), and it works pretty well.
app = FastAPI()
@app.get("/hello/{number}/")
def hello_world_number(number: int):
return {"msg": "Hello World Number", "number": number}
If you have an endpoint like this you can simply use
In: app.url_path_for("hello_world_number", number=3)
In: app.url_path_for("hello_world_number", number=50)
Out: /hello/3/
Out: /hello/50/
In FastAPI, APIRouter and FastAPI(APIRoute) inherits from Router(Starlette's) so, if you have an APIRouter like this, you can keep using this feature
router = APIRouter()
@router.get("/hello")
def hello_world():
return {"msg": "Hello World"}
In: router.url_path_for("hello_world")
Out: /hello
Solution 3:
url_for exists, but is provided by starlette, the server underpinning FastApi: https://www.starlette.io/routing/#reverse-url-lookups