Description
Is it possible to get the return of a dependency callable declared at the decorator level?
Let me clarify why I need this with the following code, borrowed and modified from the bigger-aplications tutorial:
from fastapi import Depends, FastAPI, Header, HTTPException
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
return "Foobar"
app.include_router(
items.router,
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)
My application has a lot of endpoints, and every one, except for the "token", has the same dependency, which is a jwt token obtained from the "token" endpoint, sent as a header to the other endpoints, that must be validated. So far, so good, except that I need, besides validating the token, getting the user information obtained from parsing that token. Following the code above, it would be something like, inside the items module, retrieving somehow that "Foobar" return from get_token_header. My "items" router(module) would look something like the code bellow.
from fastapi import APIRouter, HTTPException
router = APIRouter()
@router.get("/")
async def read_items():
do something with the get_token_header defined as a dependency in the include_router level
The only alternative I see would be defining the dependency callable elsewhere and import and use it in every router and every path and subpath operation, which I think is too much code repetition.
Besides, can I access that "responses", defined at the include_router level? Or it is just for documentation in OpenAPI format?
@jonDel I don't think there is a way to access the included depedencies from the list included in the router (but I could be wrong). If you need to access it in each endpoint call, I think you have two options:
request.state in a middleware, similar to how the session middleware works in the docs. Then include starlette.Request in the signature of your endpoints and pull it there. This still involves repeatedly adding something to the signature, but at least it is less.There may be another approach I'm missing, but that's all I can think of at the moment.
I just went through figuring out this for a current project. The way we've come to understand it is that route-based dependencies are for doing things while class/function dependencies are for getting things. If you need something in the endpoint, you have to declare that dependency within the endpoint itself.
The route based dependencies are better for things like setting up some static data, or evaluating a token for authorization.
@wozniakty that's what I ended up doing.
Thanks for the discussion here!
You can declare the same dependency at the router or decorator level and then declare it again in the path operation to get its value.
It won't be evaluated twice, it doesn't add any extra computation, there's a dependency cache in place that stores the values of already solved dependencies for a given request (you can also disable the cache for a specific dependency, but that's not what you need here).
So yeah, declare it at the top level to ensure everything in a router has the same security, and then at the path operation level again to get the solved value if you need it.
@tiangolo Thanks for the clarification about the caching, that helps ease some concerns. I think it makes sense to build dependencies then to return what you need, and reuse them as necessary.
went on this as well refactoring things,
I just went through figuring out this for a current project. The way we've come to understand it is that route-based dependencies are for doing things while class/function dependencies are for getting things. If you need something in the endpoint, you have to declare that dependency within the endpoint itself.
The route based dependencies are better for things like setting up some static data, or evaluating a token for authorization.
this ^^^^^^ rocks thanks @wozniakty !
This is the best explanation on how to reason about dependencies defined at the router level (in include_router) vs those defined at the endpoint level,
We decided to named them set_dependency_at_router_level because they are doing things to paraphrase @wozniakty vs get_dependency_at_endpoint_level because they are needed to get stuff and this proved to be explicit enough for newcomers
, at least in our case
Cool! I think the original issue is now solved, right?
May we close it @jonDel ?
Done!
Most helpful comment
Thanks for the discussion here!
You can declare the same dependency at the router or decorator level and then declare it again in the path operation to get its value.
It won't be evaluated twice, it doesn't add any extra computation, there's a dependency cache in place that stores the values of already solved dependencies for a given request (you can also disable the cache for a specific dependency, but that's not what you need here).
So yeah, declare it at the top level to ensure everything in a router has the same security, and then at the path operation level again to get the solved value if you need it.