Search code examples
pythonfastapistarlette

Add route to FastAPI with custom path parameters


I am trying to add routes from a file and I don't know the actual arguments beforehand so I need to have a general function that handles arguments via **kwargs.

To add routes I am using add_api_route as below:

from fastapi import APIRouter

my_router = APIRouter()

def foo(xyz):
    return {"Result": xyz}

my_router.add_api_route('/foo/{xyz}', endpoint=foo)

Above works fine.

However enrty path parameters are not fixed and I need to read them from a file, to achieve this, I am trying something like this:

from fastapi import APIRouter

my_router = APIRouter()

def foo(**kwargs):
    return {"Result": kwargs['xyz']}

read_from_file = '/foo/{xyz}' # Assume this is read from a file

my_router.add_api_route(read_from_file, endpoint=foo)

But it throws this error:

{"detail":[{"loc":["query","kwargs"],"msg":"field required","type":"value_error.missing"}]}

FastAPI tries to find actual argument xyz in foo signature which is not there.

Is there any way in FastAPI to achieve this? Or even any solution to accept a path like /foo/... whatever .../?


Solution

  • This will generate a function with a new signature (I assume every parameter is a string):

    from fastapi import APIRouter
    import re
    import inspect
    
    my_router = APIRouter()
    
    
    def generate_function_signature(route_path: str):
        args = {arg: str for arg in re.findall(r'\{(.*?)\}', route_path)}
        
        def new_fn(**kwargs):
            return {"Result": kwargs['xyz']}
    
        params = [
            inspect.Parameter(
                param,
                inspect.Parameter.POSITIONAL_OR_KEYWORD,
                annotation=type_
            ) for param, type_ in args.items()
        ]
    
        new_fn.__signature__ = inspect.Signature(params)
        new_fn.__annotations__ = args
        return new_fn
    
    
    read_from_file = '/foo/{xyz}' # Assume this is read from a file
    
    my_router.add_api_route(
        read_from_file,
        endpoint=generate_function_signature(read_from_file)
    )
    
    

    However I am sure there is a better way of doing whatever you are trying to do, but I would need to understand your problem first