FastAPI: How to get raw URL path from request?

Question:

I have a GET method with requested parameter in path:

@router.get('/users/{user_id}')
async def get_user_from_string(user_id: str):
    return User(user_id)

Is it possible to get base url raw path (i.e., '/users/{user_id}') from the request?

I have tried to use the following way:

path = [route for route in request.scope['router'].routes if
        route.endpoint == request.scope['endpoint']][0].path

But it doesn’t work and I get:

AttributeError: ‘Mount’ object has no attribute ‘endpoint’

Asked By: Kirill Marks

||

Answers:

Update

If what you need is the original route path, i.e., /users/{user_id}, you could use the below. The way it works is by getting the root_path first—which would normally be an empty string, unless you have mounted sub-application(s) to the top-level app (e.g., app.mount("/subapi", subapi)), and hence, you need the result to be prefixed with that specific path /subapi—and then append to it the route’s path , which you can get from the APIRoute object. Example:

from fastapi import Request
    
@app.get('/users/{user_id}')
def get_user(user_id: str, request: Request):
    path = request.scope['root_path'] + request.scope['route'].path
    return path

Output:

/users/{user_id}

Original answer

As per FastAPI documentation:

As FastAPI is actually Starlette underneath, with a layer of several
tools on top, you can use Starlette’s Request object directly when you
need to.

Thus, you can use Request object to get the URL path. For instance:

from fastapi import Request

@app.get('/users/{user_id}')
def get_user(user_id: str, request: Request):
    return request.url.path

Output (if the received user_id was 1):

/users/1 
Answered By: Chris

You can use the APIRout object property in the request to get the actual path

example:

raw_path = request.scope['route'].path 
#'/user/{id}'
Answered By: qiujiong

Having found both the answers to not work I’ll share what I use.
It’s not great as if there’s shared values in path params it will not work

path = request.url.path
for key, val in request.path_params.items():
    path = path.replace(val, F'{{{key}}}')
Answered By: Fraser Langton

I’m working on implementing this for OpenTelemetry and the way to get the original route with the data that’s available is as follows:

def get_route_from_request(req):
  root_path = req.scope.get("root_path", "")

  route = scope.get("route")
  if not route:
    return None
  path_format = getattr(route, "path_format", None)
  if path_format:
    return f"{route_path}{path_format}"

  return None

Note that the accepted answer is not returning what was asked, as it returns the path as received by the server.

None of the other answers deal with mounted apps.

And finally, answers checking the name of the endpoint are also wrong as the same function could be used in different endpoints.

Answered By: Sebastian Kreft

The below solution worked fine for me
using the string replace with count parameter replaces the first occurence only. And request.path_params will return the path parameters in the sequence you take it in the request.

def get_raw_path(request):
    path = request.url.path
    for key, val in request.path_params.items():
        path = path.replace(val, F'{{{key}}}',1)
    return path
Answered By: Ashutosh Singhai
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.