Skip to content

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. - dependency.protocol: The interface or protocol type. - dependency.implementation: The implementation or factory. - dependency.lifetime: The lifetime of the dependency (SINGLETON, SCOPED, TRANSIENT).

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. - dependency.dependency: The callable that will produce the actual instance. - dependency.use_cache: Whether the instance should be cached for reuse.

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.

  • If a key is registered in the container, it will be replaced with the registered dependancy, while the value remains the provided override callable.
  • Keys not registered in the container are left unchanged.
  • This means you can mix normal FastAPI overrides with container-based dependencies without conflict.
{}
container Optional[Container]

An optional secondary container (e.g., a test or mock container).

  • For each protocol in this container that is also registered in the main container, the resolved dependency from the main container will be used as the key, and the dependency from the secondary container will be used as the value.

  • Protocols in the secondary container that are not registered in the main container are ignored.

  • NOTE: The lifetime of each dependency should follow the main container, unless you know exactly what you are doing.

      • For SCOPED or FACTORY dependencies in the main container, the original lifetime is always preserved regardless of what is registered in the secondary container (except SINGLETON).
      • For SINGLETON dependencies in the main container: if the main container has SINGLETON and the secondary container has a different lifetime, the resulting lifetime will be SCOPED;
      • If the main container has a non-SINGLETON lifetime and the secondary container registers it as SINGLETON, the resulting lifetime will be SINGLETON.
None

Returns:

Type Description
dict[Callable[..., Any], Callable[..., Any]]

dict[Callable[..., Any] | Depends, Callable[..., Any]]: A new dictionary suitable for assigning to app.dependency_overrides.

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.