Dependency Injection¶
Ushka includes a simple but powerful Dependency Injection (DI) container that helps you manage the dependencies of your application in a clean and decoupled way.
The DI container is responsible for creating and managing the lifecycle of your "services".
What is a Service?¶
In Ushka, a service is typically a class that encapsulates a piece of business logic or functionality. For example, you might have a UserService that handles user-related operations, or a PaymentGateway service that interacts with a payment provider.
Automatic Injection in Route Handlers¶
The most common way you'll interact with the DI container is by simply "asking for" your services in your view functions. You do this by adding a parameter to your function with a type hint for the service you need. Ushka's router will automatically resolve the service from the container and pass it in as an argument.
Example:
Let's imagine we have a ProductService. In your view, you can just ask for it:
# in apps/products/views.py
from ushka.http import Request
from .services import ProductService # Assume ProductService is in services.py
async def get_all(request: Request, service: ProductService):
# The `service` parameter will be an instance of ProductService,
# automatically provided by the DI container.
all_products = await service.get_all_products()
return {"products": all_products}
This makes your code clean, easy to test, and decoupled from the specifics of how ProductService is created.
Creating and Registering Services¶
Ushka automatically discovers and registers services for you.
- Create a
services.pyfile inside any of your app directories (e.g.,apps/products/services.py). - Define your service class in that file.
# in apps/products/services.py
class ProductService:
def __init__(self):
# This service has no dependencies of its own, but if it did,
# the container would inject them here. For example:
# def __init__(self, db_connection: DBConnection):
# self.db = db_connection
pass
async def get_all_products(self):
# In a real app, this would query the database
return [
{"id": 1, "name": "Super Gadget"},
{"id": 2, "name": "Mega Widget"},
]
That's it! As long as the products app is listed in your ushka.toml, Ushka will find ProductService and make it available for injection.
Service Scopes¶
By default, all services are registered as Singletons. This means the DI container creates only one instance of the service for the entire application lifecycle. This is efficient for services that are stateless or manage a shared resource.
You can change this behavior by adding a __service_scope__ attribute to your service class.
TRANSIENT Scope¶
If you want a new instance of your service to be created for every single request, you can set the scope to TRANSIENT.
from ushka.core.di import ServiceScope
class MyRequestScopedService:
__service_scope__ = ServiceScope.TRANSIENT
def __init__(self):
# A new instance of this class will be created for each request
# that requires it.
pass
The available scopes are:
* ServiceScope.SINGLETON (default)
* ServiceScope.TRANSIENT