28

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?

4
  • 1
    You can make your dependency depend on a path parameter, effectively doing Depends(item_for_client_from_path) and having item_for_client_from_path depend on `item_for_client_from_path(item_id=Path(), session=Depends(security.user_session)); i.e. you make dependencies that abstract away those subdependencies that you use each time. Does that match what you're looking for? Commented Aug 5, 2021 at 14:34
  • @MatsLindh it might. from your comment I can't see how you would extract the parameter from the Path(). I'm new(ish) to FastAPI so I most likely missed something obvious. As in the example above I need to extract a part of the path which may not be in an entirely uniform position. For some functions there may be other items before /item/{item_id}/ ... /foo/bar/item/{item_id}/. Commented Aug 5, 2021 at 15:57
  • As long as the parameter is named item_id in both locations it'll work as you expect. I'll try to make an example when I have time Commented Aug 5, 2021 at 19:08
  • That'd be awesome thanks! Commented Aug 5, 2021 at 19:09

3 Answers 3

52

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/

Sign up to request clarification or add additional context in comments.

4 Comments

That's really great thanks. Regarding doing it on the router, I thought the return value of dependencies defined like that discarded. Obviously I can make the dependency function attach the result to Request.state. I was just wondering if I'd missed a more elegant approach.
Oh, no wait. I think this explains how. Thanks very much for your help!
What if my_dependency_function needs another parameters like a database service?
@maudev Well ideally you would have a dependency function which creates a database connection, at which point just call that function from within whatever dependency needs it. That is what I would do.
3

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))]

Comments

1

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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.