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 since router isn't an instance of FastAPI class. That is, we must have the FastAPI 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