Reputation: 14924
Is there any way for a FastAPI "dependency" to interpret Path parameters?
I have a lot of functions of the form:
@app.post("/item/{item_id}/process", response_class=ProcessResponse)
async def process_item(item_id: UUID, session: UserSession = Depends(security.user_session)) -> ProcessResponse:
item = await get_item(client_id=session.client_id, item_id=item_id)
await item.process()
Over and over, I need to pass in [multiple] arguments to fetch the required item before doing something with it. This is very repetitive and makes the code very verbose. What I'd really like to do is pass the item
in as an argument to the method.
Ideally I'd like to make get_item
a dependency or embed it somehow in the router. This would dramatically reduce the repetitive logic and excessively verbose function arguments. The problem is that some critical arguments are passed by the client in the Path.
Is it possible to pass Path arguments into a dependency or perhaps execute the dependency in the router and pass the result?
Upvotes: 27
Views: 32390
Reputation: 97
It's been a while, but since it can still be useful to someone... If you want to pass an extra argument you can do so by using a lambda function. In my case I wanted to use the response_model in a validation, so I had something like this:
param: Annotated[str, Depends(lambda param: validate_func(OutputModel, param))]
Upvotes: 1
Reputation: 146
There is also possibility to achieve the same via class based approach
First, Create your dependency like so
class ItemIdExtractor:
async def __call__(self, item_id: int = Path()) -> int | None:
# DO SOMETHING HERE IF NEEDED. MAYBE VALIDATE ETC.
return item_id
ItemIdDep = Depends(ItemIdExtractor())
Then you can use it in any path operation you want
@app.get("/items/{item_id}")
async def retrieve_item(item_id: int = ItemIdDep):
print(item_id)
return Response(status_code=status.HTTP_200_OK)
Upvotes: 1
Reputation: 1984
A FastAPI dependency function can take any of the arguments that a normal endpoint function can take.
So in a normal endpoint you might define a path parameter like so:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
Now if you want to use that parameter in a dependency, you can simply do:
from fastapi import Depends, FastAPI
app = FastAPI()
async def my_dependency_function(item_id: int):
return {"item_id": item_id}
@app.get("/items/{item_id}")
async def read_item(item_id: int, my_dependency: dict = Depends(my_dependency_function)):
return my_dependency
The parameters will simply be passed on through to the dependency function if they are present there. You can also use things like Path
and Query
within the dependency function to define where these are coming from.
It will just analyze the request object to pull these values.
Here is an example using the Path
function from FastAPI:
from fastapi import Depends, FastAPI, Path
app = FastAPI()
async def my_dependency_function(item_id: int = Path(...)):
return {"item_id": item_id}
@app.get("/items/{item_id}")
async def read_item(my_dependency: dict = Depends(my_dependency_function)):
return my_dependency
As for your concern of implementing it as a dependency in the router, you can do something like this when creating the router:
items_router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(my_dependency_function)],
)
Or you can do it when you run include_router
on the app like:
app.include_router(
items_router,
prefix="/items",
dependencies=[Depends(my_dependency_function)],
)
For more on dependencies and more examples like this see https://fastapi.tiangolo.com/tutorial/dependencies/
Upvotes: 45