3

I have a FastAPI app that mostly calls external apis. In some instances, a path operation will make several calls to the same host. For that reason, I want to use a single httpx AsyncClient for each request.

The correct way to do this seems to be yielding the client within a depepdency:

async def get_client(token: str):

    client = httpx.AsyncClient(headers={"Authorization": f"Bearer {token}"})
    
    try:
        yield client
    
    finally:
        await client.aclose()

I do not want to add this dependency to every path operation though, purely for the sake of not repeating myself throughout the code. However, having read https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/, it seems global dependencies are only applicable if that dependency is doing something, rather than returning something. I cannot see how each path operation accesses the yielded client unless the dependency is specified explicitly for that particular path operation. I also cannot see how to pass the token to the dependency.

i.e. this will not allow my path operations to use the client:

app.include_router(
    router,
    prefix="/api",
    dependencies=Depends(get_client)
)

but this will:

@router.get("/", status_code=status.HTTP_200_OK)
async def get_something(request: Request, client: httpx.AsyncClient = Depends(get_client)):

    client.get('www.google.com')

Is it possible to use global dependencies if that dependency returns something that is required by the path operation?

notxapw4
  • 83
  • 1
  • 7
  • As workaround, you could use [class based views](https://stackoverflow.com/questions/63853813/how-to-create-routes-with-fastapi-within-a-class) – alex_noname Nov 03 '21 at 14:56
  • Where does the token come from? You could also use the `state` object present on the request object to store a reference there, but `Depends` usually works fine (the `get_client` can in turn have a dependency on `get_current_token` or something like that) – MatsLindh Nov 03 '21 at 22:20

1 Answers1

0

I am under the impression that FastAPI dependencies are not the right for this class of problems. I neither want class based views (as suggested in the comments), nor do I want to add a repetitive parameter to all routes. Instead, I will just use a global httpx client instance that can be imported everywhere needed.

So I created a module http which exports get() and post() functions. As well as closing all connections via .aclose() when the FastAPI shuts down.

from httpx import AsyncClient

from .app import app


async def get(*args, **kwargs):
    return await HTTPX_CLIENT.get(*args, **kwargs)


async def post(*args, **kwargs):
    return await HTTPX_CLIENT.post(*args, **kwargs)


@app.on_event("shutdown")
async def shutdown_event():
    await HTTPX_CLIENT.aclose()


HTTPX_CLIENT = AsyncClient()

so that any route can import it and make requests using that single httpx client:

from .http import post


@app.get("/")
async def root():
    await post('http://host', json=dict(hello="world"))
    return {"message": "I just posted!"}
Lars Blumberg
  • 19,326
  • 11
  • 90
  • 127
  • FastAPI does support a global dependency but it is unclear to me from the docs how an api function can then access this value https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/ – Austin Hallett Jun 06 '23 at 20:39