Dependency Resolution
Once you've registered your dependencies, you can resolve and use them in your FastAPI endpoints. FastIoC makes this incredibly simple - just add type hints to your endpoint parameters, and the container automatically injects the registered implementations.
Simple Resolution
The most straightforward way to resolve dependencies is by type-hinting your endpoint parameters with the registered interface:
# main.py
from fastapi import FastAPI
from project.interfaces import IUserService
app = FastAPI()
container.injectify(app)
@app.get('/user/{user_id}')
def get_user_endpoint(user_id: int, service: IUserService) -> dict:
# service is automatically injected with the registered implementation
return service.get_user(user_id)
# project/interfaces.py
from typing import Protocol
class IUserService(Protocol):
def get_user(self, id: int) -> dict: ...
That's it! No Depends() needed. FastIoC automatically recognizes that IUserService is registered and injects it.
Dependency Not Working?
If your endpoint returns 422 Unprocessable Entity errors, it likely means the dependency wasn't registered in the container. See Unregistered Dependencies for troubleshooting.
Annotated Resolution
For non-class dependencies (like functions or custom marker types), you can use Python's Annotated type for clearer intent:
# main.py
from typing import Annotated
from project.interfaces import IUserService
from project.dependencies import UserId, ApiKey
@app.get('/profile')
def get_profile(
user_id: Annotated[int, UserId],
api_key: Annotated[str, ApiKey]
) -> dict:
return {
"user_id": user_id,
"authenticated": True
}
# project/dependencies.py
# Marker types for function-based dependencies
class UserId(int): ...
class ApiKey(str): ...
Why use Annotated?
- Makes the dependency source explicit and clear
- Works seamlessly with any dependency type
- Provides better IDE support and documentation
- Can also be used with class-based dependencies if preferred
# This also works for classes
@app.get('/data')
def get_data(service: Annotated[IUserService, IUserService]) -> dict:
return service.get_user(1)
Mixing with FastAPI Features
FastIoC is 100% compatible with FastAPI's native features. You can freely mix registered dependencies with query parameters, path parameters, request bodies, headers, cookies, and traditional Depends():
# project/routers/items.py
from fastapi import APIRouter, Depends, Header, Cookie
from typing import Annotated
from project.interfaces import IUserService
from project.dependencies import UserId
router = APIRouter()
def get_request_id() -> str:
"""A regular FastAPI dependency (not registered in container)"""
return "req-12345"
@router.get('/items/{item_id}')
async def get_item(
# Path parameter
item_id: int,
# Query parameters
q: str,
page_size: int = 10,
# FastIoC registered dependency (simple)
service: IUserService,
# FastIoC registered dependency (annotated)
user_id: Annotated[int, UserId],
# Traditional FastAPI dependency
request_id: str = Depends(get_request_id),
# Headers and cookies
authorization: Annotated[str, Header()],
session_token: Annotated[str, Cookie()]
) -> dict:
return {
"item_id": item_id,
"query": q,
"page_size": page_size,
"user": service.get_user(user_id),
"request_id": request_id,
"auth": authorization,
"session": session_token
}
Everything works together seamlessly!
Async Endpoints
Both sync and async endpoints work identically:
# project/routers/users.py
from project.interfaces import IUserService
@router.get('/sync')
def sync_endpoint(service: IUserService) -> dict:
return service.get_user(1)
@router.get('/async')
async def async_endpoint(service: IUserService) -> dict:
# Same syntax, FastAPI handles execution automatically
return service.get_user(1)
Whether your dependency implementation is sync or async, FastAPI automatically handles the execution.
Next Steps
- Understand Passive Dependencies - using dependencies in route decorators
- Learn about Dependency Lifetimes - singleton, scoped, and transient behaviors
- Explore Nested Dependencies - automatic resolution of dependency chains