Option 1
One way is to make a partial application of the function using functools.partial. As per functools.partial's documentation:
functools.partial(func, /, *args, **keywords)
Return a new partial object which when called will behave like
func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they
are appended to args. If additional keyword arguments are supplied,
they extend and override keywords. Roughly equivalent to:
def partial(func, /, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = {**keywords, **fkeywords}
return func(*args, *fargs, **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
The partial() is used for partial function application which "freezes"
some portion of a function's arguments and/or keywords resulting in a
new object with a simplified signature.
Working Example
Here is the source for the add_route() method, as well as the part in Route class where Starlette checks if the endpoint_handler that is passed to add_route() is an instance of functools.partial.
Note that the endpoint has to return an instance of Response/JSONResponse/etc., as returning a str or dict object (e.g., return client_name), for instance, would throw TypeError: 'str' object is not callable or TypeError: 'dict' object is not callable, respectively. Please have a look at this answer for more details and examples on how to return JSON data using a custom Response.
from fastapi import FastAPI, Request, APIRouter, Response
from functools import partial
async def my_endpoint(request: Request, client_name: str ='my_client'):
print(request.method)
return Response(client_name)
app = FastAPI()
router = APIRouter()
router.add_route('/', partial(my_endpoint, client_name='my_other_client'), ['GET'])
app.include_router(router)
Option 2
As noted by @MatsLindh in the comments section, you could use a wrapper/helper function that returns an inner function, which is essentially the same as using functools.partial in Option 1, as that is exactly how that function works under the hood (as shown in the quote block earlier). Hence, throught the wrapper function you could pass the parameters of your choice to the nested function.
Working Example
from fastapi import FastAPI, Request, APIRouter, Response
def my_endpoint(client_name: str ='my_client'):
async def newfunc(request: Request):
print(request.method)
return Response(client_name)
return newfunc
app = FastAPI()
router = APIRouter()
router.add_route('/', my_endpoint(client_name='my_other_client'), ['GET'])
app.include_router(router)
I would also suggest having a look at this answer and this answer, which demonstrate how to use add_api_route() instead of add_route(), which might be a better alternative, if you faced issues when using FastAPI dependencies.
def client(client_name): async def wrapped(): print(client_name) ... return wrapped); this will bind the givenclient_nameto the function that gets returned (which has the given values in the scope). This also matches that the API requirement for the function is (no parameters), since client_name is given when creating the api.clientnamevalues to different routes.:and=the function works both ways. It returns the expected payload when called as in my first example.