Rajesh Kumar Dash
Rajesh Kumar Dash

Reputation: 2277

How can I get every route path from FastAPI app?

I am new to FastAPI and Python. I need to get all the routes on my root path and show it to the user. However, I could not find a way to get all the paths recursively. The API is versioned with the help of VersionedFastAPI and the current code does not give the path inside version; it just returns generic ones.

FastAPI backend:

app = FastAPI()
router = APIRouter(
    tags=["utilities"]
)

@router.get("/")
def read_root(request: Request):
    url_list = [
        route.path
        for route in request.app.routes
    ]
    return { "endpoints": set(url_list) }

@app.get('/foo')
@version(1)
def foo():
    return "foo V1"

@app.get('/foo')
@version(2)
def foo():
    return "foo V2"

app = VersionedFastAPI(app, enable_latest=True, version_format='{major}', prefix_format='/v{major}')
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"]
)

app.include_router(router)

Code for getting the path list found under \ route

 url_list = [
            route.path
            for route in request.app.routes
        ]
        return { "endpoints": set(url_list) }

This returns only:

["/v1/openapi.json","/v2/docs","/openapi.json","/v2/openapi.json","/v2","/","/redoc","/v1","/docs","/docs/oauth2-redirect","/v1/docs","/latest"]

However /foo end point is missing. Any clue on this will help.

Upvotes: 3

Views: 4477

Answers (1)

Chris
Chris

Reputation: 34600

In the example below, make sure to call get_routes() before passing the FastAPI instance to VersionedFastAPI. Also, there is an endpoint (i.e., /greet) with no version specified. Thus, to make sure that such endpoints—if happen to exist in your API— will be assigned a version, you wpuld need to define a default value (might as well be the latest version) when attempting to get the version of the endpoint at this line of code: version = getattr(route.endpoint, "_api_version", (2, 0)). By accessing the OpenAPI/Swagger UI documentation at http://127.0.0.1:8000/v1/docs and http://127.0.0.1:8000/v2/docs, you will notice that /greet appears in both versions of the API; hence, it can be accessed by either using /v1/greet or /v2/greet. That is because it hadn't been given any specific version initially; however, using either endpoint, requests will be dispatched to the same path operation function.

Working Example

from fastapi import FastAPI, APIRouter
from fastapi_versioning import VersionedFastAPI, version
import uvicorn


app = FastAPI()
router = APIRouter()
all_routes =[]


def get_routes():
    reserved_routes = ["/openapi.json", "/docs", "/docs/oauth2-redirect", "/redoc"]
    for route in app.routes:
        if route.path not in reserved_routes:
            if route.name is not None:
                version = getattr(route.endpoint, "_api_version", (2, 0))
                all_routes.append("/v" + str(version[0]) + route.path)


@router.get("/")
def index():
    return { "endpoints": all_routes }

 
@app.get("/foo")
@version(1)
def foo():
    return "foo v1"


@app.get("/foo")
@version(2)
def foo():
    return "foo v2"


@app.get("/items/{item_id}")
@version(2)
def get_item(item_id: int):
    return item_id
    

@app.get("/greet")
def greet_with_hi():
    return "Hi"


get_routes()
app = VersionedFastAPI(app, version_format='{major}',prefix_format='/v{major}')
app.include_router(router)


if __name__ == '__main__':
    uvicorn.run(app, host='127.0.0.1', port=8000)

Output (when accessing http://127.0.0.1:8000/):

{"endpoints":["/v1/foo","/v2/foo","/v2/items/{item_id}","/v2/greet"]}

This answer might also prove helpful, regarding retrieving the raw route path.

Upvotes: 3

Related Questions