Add route to FastAPI with custom path parameters
Question:
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 .../
?
Answers:
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
As described here, you can use the path
convertor, provided by Starlette, to capture arbitrary paths. Example:
from fastapi import APIRouter, FastAPI
app = FastAPI()
my_router = APIRouter()
def foo(rest_of_path: str):
return {"rest_of_path": rest_of_path}
route_path = '/foo/{rest_of_path:path}'
my_router.add_api_route(route_path, endpoint=foo)
app.include_router(my_router)
Input test:
http://127.0.0.1:8000/foo/https://placebear.com/cache/395-205.jpg
Output:
{"rest_of_path":"https://placebear.com/cache/395-205.jpg"}
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 .../
?
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
As described here, you can use the path
convertor, provided by Starlette, to capture arbitrary paths. Example:
from fastapi import APIRouter, FastAPI
app = FastAPI()
my_router = APIRouter()
def foo(rest_of_path: str):
return {"rest_of_path": rest_of_path}
route_path = '/foo/{rest_of_path:path}'
my_router.add_api_route(route_path, endpoint=foo)
app.include_router(my_router)
Input test:
http://127.0.0.1:8000/foo/https://placebear.com/cache/395-205.jpg
Output:
{"rest_of_path":"https://placebear.com/cache/395-205.jpg"}