Dependencies#
FastStream uses the secondary library FastDepends for dependency management. This dependency system is literally borrowed from FastAPI, so if you know how to work with that framework, you'll be comfortable with dependencies in FastStream.
You can visit the FastDepends documentation for more details, but the key points and additions are covered here.
Type Casting#
The key function in the dependency management and type conversion system in FastStream is the decorator @apply_types
(also known as @inject
in FastDepends).
By default, it applies to all event handlers, unless you disabled the same option when creating the broker.
Warning
Setting the apply_types=False
flag not only disables type casting but also Depends
and Context
.
If you want to disable only type casting, use serializer=None
instead.
This flag can be useful if you are using FastStream within another framework and you need to use its native dependency system.
Using Annotated
#
Dependencies also can be used with Annotated
.
from faststream import Depends, Logger
from faststream.kafka import KafkaBroker
broker = KafkaBroker()
async def base_dep(user_id: int) -> bool:
return True
@broker.subscriber("in-test")
async def base_handler(
user: str,
logger: Logger,
dep: bool = Depends(base_dep),
):
assert dep is True
logger.info(user)
from typing import Annotated
from faststream import Depends, Logger
from faststream.kafka import KafkaBroker
broker = KafkaBroker()
async def base_dep(user_id: int) -> bool:
return True
@broker.subscriber("in-test")
async def base_handler(
user: str,
logger: Logger,
dep: Annotated[bool, Depends(base_dep)],
):
assert dep is True
logger.info(user)
Dependency Injection#
To implement dependencies in FastStream, a special class called Depends is used
The first step: You need to declare a dependency, which can be any Callable
object.
Callable
A "Callable" is an object that can be "called". It can be a function, a class, or a class method.
In other words, if you can write code like my_object()
- my_object
is Callable
Second step: Declare which dependencies you need using Depends
The last step: Use the result of executing your dependency!
Top-level Dependencies#
If you don't need a dependency result, you can use the following code:
But, using a special subscriber
parameter is much more suitable:
You can also declare broker-level dependencies, which will be applied to all broker's handlers:
Nested Dependencies#
Dependencies can also contain other dependencies. This works in a very predictable way: just declare
Depends
in the dependent function.
- A nested dependency is called here
- A nested dependency is called here
- A nested dependency is called here
- A nested dependency is called here
- A nested dependency is called here
Auto @apply_types
In the code above, we didn't use this decorator on our simple_dependency
.
However, it still automatically applies to all functions used as dependencies.
Caching
In the example above, the another_dependency
function will be called at ONCE!
FastDepends caches all dependency execution results within ONE @apply_types
call stack.
This means that all nested dependencies will receive the cached result of dependency execution.
But, between different calls of the main function, these results will be different.
To prevent this behavior, just use Depends(..., cache=False)
. In this case, the dependency will be used for each function
in the call stack where it is used.
Use with Regular Functions#
You can use the decorator @apply_types
not only with @broker.subscriber(...)
, but also with regular functions, both synchronous and asynchronous.
Be careful
In asynchronous code, you can use both synchronous and asynchronous dependencies. But in synchronous code, only synchronous dependencies are available to you.
Casting Dependency Types#
FastDepends, used by FastStream, also gives the type return
. This means that the value returned by the dependency will be
cast to the type twice: as return
for dependencies and as the input argument of the main function. This does not incur additional costs if
these types have the same annotation. Just keep it in mind. Or not... Anyway, I've warned you.
Also, the result of executing the dependency is cached. If you use this dependency in N
functions,
this cached result will be converted to type N
times (at the input to the function being used).
To avoid problems with this, use mypy or just be careful with the annotation of types in your project.