Container
FastIoC Container module.
Provides a dependency injection IoC container for registering and resolving dependencies with different lifetimes (singleton, request-scoped, transient) in FastAPI.
Container
Dependency Injection IoC container for registering and resolving dependencies in FastAPI applications.
Supports three lifetimes
- Singleton: One single shared instance per process/worker.
- Scoped: One instance per HTTP request, reused during that request.
- Factory: A new instance each time the dependency is resolved.
Attributes:
Name | Type | Description |
---|---|---|
dependencies |
dict[type, Depends]
|
Maps protocol types to FastAPI Depends instances. |
add_scoped(protocol: type, implementation: FastIoCConcrete)
Register a request-scoped dependency.
A new instance is created for each HTTP request and reused throughout that request.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
protocol
|
type
|
The interface or protocol type that acts as the key for resolving this dependency. |
required |
implementation
|
FastIoCConcrete
|
The actual implementation to be provided when the protocol is resolved. |
required |
Raises:
Type | Description |
---|---|
ProtocolNotRegisteredError
|
If a nested dependency is not registered. |
add_singleton(protocol: type, implementation: FastIoCConcrete)
Register a singleton dependency.
One single shared instance will be used throughout the entire process/worker.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
protocol
|
type
|
The interface or protocol type that acts as the key for resolving this dependency. |
required |
implementation
|
FastIoCConcrete
|
The actual implementation to be provided when the protocol is resolved. |
required |
Raises:
Type | Description |
---|---|
SingletonGeneratorNotAllowedError
|
If 'implementation' is a generator or async generator. |
ProtocolNotRegisteredError
|
If a nested dependency is not registered. |
add_transient(protocol: type, implementation: FastIoCConcrete)
Register a transient dependency.
A new instance is created each time the dependency is resolved.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
protocol
|
type
|
The interface or protocol type that acts as the key for resolving this dependency. |
required |
implementation
|
FastIoCConcrete
|
The actual implementation to be provided when the protocol is resolved. |
required |
Raises:
Type | Description |
---|---|
ProtocolNotRegisteredError
|
If a nested dependency is not registered. |
before_register_hook(dependency: Dependency[Any]) -> Dependency[Any]
Hook executed before a dependency is registered in the container.
This method allows you to inspect, modify, or validate the dependency before it is stored in the container.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
dependency
|
Dependency[Any]
|
The dependency instance about to be registered.
- |
required |
Returns:
Type | Description |
---|---|
Dependency[Any]
|
Dependency[Any]: The (optionally modified) dependency that will actually be registered. |
Usage
You can override this method to implement custom logic such as: - Logging registration - Wrapping the implementation class - Validating dependency types - Applying decorators or configuration
Example (monkey patch):
def my_register_hook(dep):
print(f"Registering {dep.protocol.__name__} -> {dep.implementation}")
return dep
container = Container()
container.before_register_hook = my_register_hook # Monkey patch the hook
container.add_scoped(IService, Service)
# Now each register prints info before storing the dependency
before_resolve_hook(dependency: Depends) -> Depends
Hook executed before a dependency is resolved from the container.
This method allows you to inspect, wrap, or modify the dependency just before it is provided to a consumer (endpoint, service, etc).
Parameters:
Name | Type | Description | Default |
---|---|---|---|
dependency
|
Depends
|
The FastAPI Depends instance representing the dependency
about to be resolved.
- |
required |
Returns:
Name | Type | Description |
---|---|---|
Depends |
Depends
|
The (optionally modified) Depends instance to be used in resolution. |
Usage
You can override this method to implement custom logic such as: - Logging resolution - Wrapping the callable with additional behavior - Injecting default parameters - Applying metrics or tracing
Example (monkey patch):
def my_resolve_hook(dep):
print(f"Resolving dependency {dep.dependency.__name__}")
return dep
container = Container()
container.before_resolve_hook = my_resolve_hook # Monkey patch the hook
# Now, when resolving a dependency:
resolved_dep = container.resolve(IService)
# Each resolve prints info before returning the instance
check_if_registered(protocol: type)
Raises ProtocolNotRegisteredError if the protocol is not registered.
dispose(app: Optional[FastAPI] = None)
async
Dispose all registered singleton dependencies.
This method calls the disposal function of each singleton that was registered in the container. Both synchronous and asynchronous disposal functions are supported. If a disposal function raises an exception, it will be caught and logged, and the disposal process will continue for the remaining singletons.
Logging format
'Error disposing "ClassName": exception'
Notes
- Async disposal functions will be awaited.
- Errors during disposal do not prevent other singletons from being disposed.
injectify(*targets: FastAPI | APIRouter)
Wrap a FastAPI app or APIRouter to automatically inject dependencies.
THIS MUST BE CALLED BEFORE ADDING ANY ENDPOINTS THAT REQUIRE DEPENDENCY INJECTION.
This function overrides add_api_route
to:
- Inject dependencies into endpoint parameters using the provided container.
- Resolve route-level dependencies automatically.
- Support both FastAPI application and APIRouter instances.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
*targets
|
FastAPI | APIRouter
|
The FastAPI apps or APIRouters to wrap. |
()
|
Examples:
app = FastAPI()
container = Container()
container.injectify(app)
override(dependencies: dict[Callable[..., Any], Callable[..., Any]] = {}, container: Optional[Container] = None) -> dict[Callable[..., Any], Callable[..., Any]]
Generate an updated dictionary suitable for FastAPI's dependency_overrides
.
This method allows merging and overriding dependencies from two sources: 1. A dictionary of user-provided overrides. 2. An optional secondary FastIoC container (e.g., a mock container for testing).
NOTE: The lifetime of each dependency is preserved: overridden dependencies are injected with the SAME LIFETIME AS ORIGINAL CONTAINER REGISTERATION.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
dependencies
|
dict[Callable[..., Any], Callable[..., Any]]
|
A dictionary where keys are the original dependency callables or protocol types that may have been registered in the container, and values are the new callables that should override them.
|
{}
|
container
|
Optional[Container]
|
An optional secondary container (e.g., a test or mock container).
|
None
|
Returns:
Type | Description |
---|---|
dict[Callable[..., Any], Callable[..., Any]]
|
dict[Callable[..., Any] | Depends, Callable[..., Any]]:
A new dictionary suitable for assigning to
|
Examples:
>>> from fastioc import Container, FastAPI
>>> app = FastAPI()
>>> container = Container()
>>> container.add_scoped(IService, Service)
>>> overrides = {
>>> IService: MockService,
>>> some_dependency: custom_callable
>>> }
>>> app.dependency_overrides.update(container.override(overrides))
register(protocol: type, implementation: FastIoCConcrete, lifetime: LifeTime)
Register a dependency with a given lifetime.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
protocol
|
type
|
The interface or protocol type that acts as the key for resolving this dependency. |
required |
implementation
|
FastIoCConcrete
|
The actual implementation to be provided when the protocol is resolved. |
required |
lifetime
|
Lifetime
|
SINGLETON, SCOPED, or FACTORY. |
required |
Raises:
Type | Description |
---|---|
SingletonGeneratorNotAllowedError
|
If a generator or async generator is registered as singleton. |
resolve(protocol: type) -> Depends
Return the Depends instance for a protocol. Raises ProtocolNotRegisteredError if not registered.