Classes as Dependencies¶
Before diving deeper into theDependency Injection system, let's upgrade the previous example.
Adict from the previous example¶
In the previous example, we were returning adict from our dependency ("dependable"):
fromtypingimportAnnotatedfromfastapiimportDepends,FastAPIapp=FastAPI()asyncdefcommon_parameters(q:str|None=None,skip:int=0,limit:int=100):return{"q":q,"skip":skip,"limit":limit}@app.get("/items/")asyncdefread_items(commons:Annotated[dict,Depends(common_parameters)]):returncommons@app.get("/users/")asyncdefread_users(commons:Annotated[dict,Depends(common_parameters)]):returncommons🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIapp=FastAPI()asyncdefcommon_parameters(q:str|None=None,skip:int=0,limit:int=100):return{"q":q,"skip":skip,"limit":limit}@app.get("/items/")asyncdefread_items(commons:dict=Depends(common_parameters)):returncommons@app.get("/users/")asyncdefread_users(commons:dict=Depends(common_parameters)):returncommonsBut then we get adict in the parametercommons of thepath operation function.
And we know that editors can't provide a lot of support (like completion) fordicts, because they can't know their keys and value types.
We can do better...
What makes a dependency¶
Up to now you have seen dependencies declared as functions.
But that's not the only way to declare dependencies (although it would probably be the more common).
The key factor is that a dependency should be a "callable".
A "callable" in Python is anything that Python can "call" like a function.
So, if you have an objectsomething (that mightnot be a function) and you can "call" it (execute it) like:
something()or
something(some_argument,some_keyword_argument="foo")then it is a "callable".
Classes as dependencies¶
You might notice that to create an instance of a Python class, you use that same syntax.
For example:
classCat:def__init__(self,name:str):self.name=namefluffy=Cat(name="Mr Fluffy")In this case,fluffy is an instance of the classCat.
And to createfluffy, you are "calling"Cat.
So, a Python class is also acallable.
Then, inFastAPI, you could use a Python class as a dependency.
What FastAPI actually checks is that it is a "callable" (function, class or anything else) and the parameters defined.
If you pass a "callable" as a dependency inFastAPI, it will analyze the parameters for that "callable", and process them in the same way as the parameters for apath operation function. Including sub-dependencies.
That also applies to callables with no parameters at all. The same as it would be forpath operation functions with no parameters.
Then, we can change the dependency "dependable"common_parameters from above to the classCommonQueryParams:
fromtypingimportAnnotatedfromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:Annotated[CommonQueryParams,Depends(CommonQueryParams)]):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponse🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:CommonQueryParams=Depends(CommonQueryParams)):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponsePay attention to the__init__ method used to create the instance of the class:
fromtypingimportAnnotatedfromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:Annotated[CommonQueryParams,Depends(CommonQueryParams)]):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponse🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:CommonQueryParams=Depends(CommonQueryParams)):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponse...it has the same parameters as our previouscommon_parameters:
fromtypingimportAnnotatedfromfastapiimportDepends,FastAPIapp=FastAPI()asyncdefcommon_parameters(q:str|None=None,skip:int=0,limit:int=100):return{"q":q,"skip":skip,"limit":limit}@app.get("/items/")asyncdefread_items(commons:Annotated[dict,Depends(common_parameters)]):returncommons@app.get("/users/")asyncdefread_users(commons:Annotated[dict,Depends(common_parameters)]):returncommons🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIapp=FastAPI()asyncdefcommon_parameters(q:str|None=None,skip:int=0,limit:int=100):return{"q":q,"skip":skip,"limit":limit}@app.get("/items/")asyncdefread_items(commons:dict=Depends(common_parameters)):returncommons@app.get("/users/")asyncdefread_users(commons:dict=Depends(common_parameters)):returncommonsThose parameters are whatFastAPI will use to "solve" the dependency.
In both cases, it will have:
- An optional
qquery parameter that is astr. - A
skipquery parameter that is anint, with a default of0. - A
limitquery parameter that is anint, with a default of100.
In both cases the data will be converted, validated, documented on the OpenAPI schema, etc.
Use it¶
Now you can declare your dependency using this class.
fromtypingimportAnnotatedfromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:Annotated[CommonQueryParams,Depends(CommonQueryParams)]):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponse🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:CommonQueryParams=Depends(CommonQueryParams)):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponseFastAPI calls theCommonQueryParams class. This creates an "instance" of that class and the instance will be passed as the parametercommons to your function.
Type annotation vsDepends¶
Notice how we writeCommonQueryParams twice in the above code:
commons:Annotated[CommonQueryParams,Depends(CommonQueryParams)]Tip
Prefer to use theAnnotated version if possible.
commons:CommonQueryParams=Depends(CommonQueryParams)The lastCommonQueryParams, in:
...Depends(CommonQueryParams)...is whatFastAPI will actually use to know what is the dependency.
It is from this one that FastAPI will extract the declared parameters and that is what FastAPI will actually call.
In this case, the firstCommonQueryParams, in:
commons:Annotated[CommonQueryParams,...Tip
Prefer to use theAnnotated version if possible.
commons:CommonQueryParams......doesn't have any special meaning forFastAPI. FastAPI won't use it for data conversion, validation, etc. (as it is using theDepends(CommonQueryParams) for that).
You could actually write just:
commons:Annotated[Any,Depends(CommonQueryParams)]Tip
Prefer to use theAnnotated version if possible.
commons=Depends(CommonQueryParams)...as in:
fromtypingimportAnnotated,AnyfromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:Annotated[Any,Depends(CommonQueryParams)]):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponse🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons=Depends(CommonQueryParams)):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponseBut declaring the type is encouraged as that way your editor will know what will be passed as the parametercommons, and then it can help you with code completion, type checks, etc:

Shortcut¶
But you see that we are having some code repetition here, writingCommonQueryParams twice:
commons:Annotated[CommonQueryParams,Depends(CommonQueryParams)]Tip
Prefer to use theAnnotated version if possible.
commons:CommonQueryParams=Depends(CommonQueryParams)FastAPI provides a shortcut for these cases, in where the dependency isspecifically a class thatFastAPI will "call" to create an instance of the class itself.
For those specific cases, you can do the following:
Instead of writing:
commons:Annotated[CommonQueryParams,Depends(CommonQueryParams)]Tip
Prefer to use theAnnotated version if possible.
commons:CommonQueryParams=Depends(CommonQueryParams)...you write:
commons:Annotated[CommonQueryParams,Depends()]Tip
Prefer to use theAnnotated version if possible.
commons:CommonQueryParams=Depends()You declare the dependency as the type of the parameter, and you useDepends() without any parameter, instead of having to write the full classagain inside ofDepends(CommonQueryParams).
The same example would then look like:
fromtypingimportAnnotatedfromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:Annotated[CommonQueryParams,Depends()]):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponse🤓 Other versions and variants
Tip
Prefer to use theAnnotated version if possible.
fromfastapiimportDepends,FastAPIapp=FastAPI()fake_items_db=[{"item_name":"Foo"},{"item_name":"Bar"},{"item_name":"Baz"}]classCommonQueryParams:def__init__(self,q:str|None=None,skip:int=0,limit:int=100):self.q=qself.skip=skipself.limit=limit@app.get("/items/")asyncdefread_items(commons:CommonQueryParams=Depends()):response={}ifcommons.q:response.update({"q":commons.q})items=fake_items_db[commons.skip:commons.skip+commons.limit]response.update({"items":items})returnresponse...andFastAPI will know what to do.
Tip
If that seems more confusing than helpful, disregard it, you don'tneed it.
It is just a shortcut. BecauseFastAPI cares about helping you minimize code repetition.







