DocsGo deeper
Dependency injection
Nexy resolves constructor dependencies for controllers and services. A class opts in with
@Injectable().
Inject a service
from nexy.decorators import Injectable
@Injectable()
class UserService:
def get_all(self) -> list[dict]:
return [{"id": 1, "name": "Ada"}]from nexy.decorators import Controller
from .user_service import UserService
@Controller(prefix="/users")
class UserController:
def __init__(self, svc: UserService) -> None:
self.svc = svc
def get(self) -> list[dict]:
return self.svc.get_all()Register the controller in a module. Listing the service in providers additionally resolves it
eagerly at startup:
from nexy.decorators import Module
from .user_controller import UserController
from .user_service import UserService
@Module()
class UsersModule:
controllers = [UserController]
providers = [UserService]How resolution works
- Each
__init__parameter is resolved from its type annotation. The annotation must be an@Injectable()class, or the parameter must have a default value; otherwise resolution raisesValueError. - Controllers are instantiated with
Container.resolve()at startup. - A dependency cycle raises
RecursionErrornaming the class. - Services can inject other services, to any depth.
Scopes
from nexy.decorators import Injectable, Scope
@Injectable(scope=Scope.REQUEST)
class CurrentUser: ...
| Scope | Lifetime |
|---|---|
Scope.SINGLETON (default) |
one instance for the whole process |
Scope.REQUEST |
one instance per request context |
Scope.TRANSIENT |
a new instance on every resolution |
The container
from nexy.decorators import Container
service = Container.resolve(UserService)
Constructing an @Injectable() class yourself raises TypeError: the container owns its lifecycle.
Use Container.resolve(...) or receive the class through another constructor.
Container.clear_request_scope() drops the current request's instances.
Route parameters
FastAPI Depends works as usual for handler parameters, next to constructor injection:
from fastapi import Depends
def get(self, limit: int = Depends(pagination)) -> list[dict]: ...
Limits
The container is process-wide. Providers are not isolated per module, exports only decides which
providers an importing module resolves eagerly, and there is no public API to swap a provider in
tests. See Nexy and NestJS for the full comparison.