Dependency Injection in Nexios
Nexios provides a modern, flexible, and powerful dependency injection (DI) system for clean, testable code. Dependencies are resolved automatically, support nesting, and can use parameter extractors for request data.
Basic Example
from nexios import NexiosApp, Depend
app = NexiosApp()
def get_settings():
return {"debug": True, "version": "1.0.0"}
@app.get("/config")
async def show_config(request, response, settings: dict = Depend(get_settings)):
return settings- Use
Depend()to mark a parameter as a dependency - Dependencies can be sync or async functions, or classes
What is Dependency Injection?
Dependency Injection (DI) is a design pattern that allows decoupling components by injecting their dependencies rather than hardcoding them. In Nexios, you declare what your handlers need, and the framework handles the rest.
Key Benefits:
- Simplicity: No complex containers or annotations
- Testability: Easy to override for isolated testing
- Reusability: Share logic across handlers
Quick Start: Basic Dependency
from nexios import NexiosApp, Depend
app = NexiosApp()
def get_database():
return DatabaseConnection()
@app.get("/users")
async def list_users(request, response, db = Depend(get_database)):
return await db.query("SELECT * FROM users")Getting Request Data: Use Parameter Extractors
For accessing query parameters, headers, and cookies in dependencies, use parameter extractors instead of Context:
Recommended: Query, Header, Cookie
from nexios import Depend, Query, Header, Cookie
def get_pagination(page: int = Query(1), limit: int = Query(10)):
return {"page": page, "limit": limit}
def get_auth_data(authorization: str = Header()):
return {"token": authorization}
def get_user_preferences(theme: str = Cookie("dark")):
return {"theme": theme}
@app.get("/dashboard")
async def dashboard(
request, response,
pagination: dict = Depend(get_pagination),
auth: dict = Depend(get_auth_data),
prefs: dict = Depend(get_user_preferences)
):
return {**pagination, **auth, **prefs}Benefits:
- Automatic type conversion
- Clean, declarative syntax
- Works in deeply nested dependencies
- Self-documenting parameters
Legacy: Context (Not Recommended)
from nexios import Depend
from nexios.dependencies import Context
def get_pagination(context: Context = None):
page = context.request.query_params.get("page", "1")
limit = context.request.query_params.get("limit", "10")
return {"page": int(page), "limit": int(limit)}Why not Context for request data?
- Manual type conversion required
- More verbose code
- Less maintainable
TIP
For request data (query, headers, cookies), use Query, Header, and Cookie parameter extractors. Reserve Context for advanced cases where you need direct access to the full request object.
Chaining & Sub-Dependencies
Dependencies can depend on other dependencies:
def get_db_config():
return {"host": "localhost", "port": 5432}
def get_db_connection(config: dict = Depend(get_db_config)):
return Database(**config)
@app.get("/users")
async def list_users(request, response, db = Depend(get_db_connection)):
return await db.query("SELECT * FROM users")Resource Management with Yield
For resources needing cleanup, use generator dependencies:
def get_db_session():
session = db.connect()
try:
yield session
finally:
session.close()
async def get_async_resource():
resource = await acquire()
try:
yield resource
finally:
await resource.release()
@app.get("/data")
async def get_data(request, response, session = Depend(get_db_session)):
return await session.query("SELECT * FROM data")Cleanup code in the finally block runs after every request, even on exceptions.
App-level and Router-level Dependencies
Apply dependencies to all routes in an app or router:
App-level Dependency
from nexios import NexiosApp, Depend
def get_tenant():
return {"tenant_id": "default"}
app = NexiosApp(dependencies=[Depend(get_tenant)])
@app.get("/config")
async def config(request, response, tenant: dict = Depend(get_tenant)):
return tenantRouter-level Dependency
from nexios import Router, Depend
router = Router(prefix="/api", dependencies=[Depend(get_auth)])
@router.get("/protected")
async def protected(request, response, auth = Depend(get_auth)):
return authUsing Classes as Dependencies
Classes with __call__ work as dependencies:
class AuthService:
def __init__(self, secret_key: str):
self.secret_key = secret_key
async def __call__(self, token: str = Header()):
return await self.verify_token(token)
auth = AuthService(secret_key="my-secret")
@app.get("/profile")
async def profile(request, response, user = Depend(auth)):
return {"message": f"Welcome {user.name}"}Real-World Example: Auth with Parameter Extractors
from nexios import NexiosApp, Depend, Query, Header, Cookie
app = NexiosApp()
def get_db():
db = connect_db()
try:
yield db
finally:
db.close()
def get_auth_user(authorization: str = Header()):
if not authorization:
return None
return verify_token(authorization)
def get_pagination(page: int = Query(1), limit: int = Query(10)):
return {"page": page, "limit": limit}
def get_user_preferences(theme: str = Cookie("dark")):
return {"theme": theme}
@app.get("/dashboard")
async def dashboard(
request, response,
db = Depend(get_db),
user = Depend(get_auth_user),
pagination = Depend(get_pagination),
prefs = Depend(get_user_preferences)
):
if not user:
return {"error": "Unauthorized"}
data = db.get_dashboard(user.id)
return {**data, **pagination, **prefs}Advanced Patterns
- Dependency Caching: Use
functools.lru_cachefor expensive dependencies - Conditional Dependencies: Use default values to make dependencies optional
- Validation: Combine with Pydantic for dependency validation
For more, see the Request Parameters guide and API reference.
