[Feature] Dependency injection without resorting to global state
See original GitHub issueHi there! I’m starting the FastAPI web server via Uvicorn after completing various initialization steps (reading command line arguments, reading a configuration file, setting up shared memory etc.). Now I would like to access said dependencies (command line arguments, config, shared memory, …) in my endpoints or, more specifically, pass them as argument to my endpoints. Unfortunately, the current way of injecting dependencies into endpoints via Depends
basically amounts to using global state, as the dependency has to be declared in the endpoint’s function signature as a Callable and so has to be available already when Python runs the semantic analysis on the code.
At the same time, app.include_router(my_router, additional_dependencies=[ ])
does not solve this issue, either, as AFAIK there is no way to access these dependencies inside my endpoints.
What’s the best practice to solve this issue? Ideally I would like to do something like
from dataclasses import dataclass
from fastapi import FastAPI, APIRouter
@dataclass
class MyConfig:
foo: str = ""
router = APIRouter()
@router.post("/")
def my_endpoint(my_config: MyConfig = DependsOnType(MyConfig)):
...
def create_app(config: MyConfig) -> FastAPI:
app = FastAPI()
app.register_dependency(type=MyConfig, value=config)
app.include_router(router, prefix="/myrouter")
return app
Then I could do app = create_app(MyConfig(foo="bar"))
to create different versions of the app, depending on the configuration and other dependencies.
EDIT: While there is a way to achieve this (see Kludex’s comment below), it has various deficiencies (see the discussion below) and I would like to propose the above functionality involving DependsOnType
and register_dependency()
as a new feature.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:5
- Comments:15 (6 by maintainers)
With the above snippet, you can just inject it as a normal dependency (and you don’t really need the function either)
This is a working example:
I worked around this limitation by using Python’s context variables.
A few lines of middleware will put them in place. The API endpoints can then either use some helper functions to get data from the context variables, possibly through
fastapi.Depends
.It’s possible for the entry point of the application, e.g., the
main()
to wrap theASGI
app in middleware like this:With this, it’s easy to define the
FastAPI
instance at the top level of a module and have it decoupled from any mutable state:Personally, I prefer to run ASGI applications using Hypercorn for this, because it allows running ASGI applications programatically from a
main()
function, which is what you need if you have an installable entry point (withsetup.py
and maybeclick
for command line parsing).@codethief: Would the above work for you? @tiangolo: What do you think about this? Would it be possible or make sense to add this to the docs somewhere? (I don’t think it’s too much boilerplate to copy/paste for the people that need this functionality. Those who need it probably want to change it anyway, and will appreciate that it can be completely typed (which is maybe not the case if we try to come up with a generic solution.)