Dependencies¶
Dependencies let you share common logic across channel handlers. They are declared with Depends and injected using
typing.Annotated.
A dependency function can be def or async def. It can also be a generator (sync or async) to provide setup and
cleanup logic.
Basic Dependency¶
Use Depends inside Annotated to tell AsyncFast to resolve a value and pass it to your handler:
from typing import Annotated
from asyncfast import AsyncFast
from asyncfast import Depends
from asyncfast import Header
app = AsyncFast()
def get_context(
request_id: Annotated[str, Header(alias="request-id")],
) -> dict[str, str]:
return {"request_id": request_id}
@app.channel("orders.created")
async def handle_orders(
context: Annotated[dict[str, str], Depends(get_context)],
) -> None:
print(context["request_id"])
{
"asyncapi": "3.0.0",
"info": {
"title": "AsyncFast",
"version": "0.1.0"
},
"channels": {
"HandleOrders": {
"address": "orders.created",
"messages": {
"HandleOrdersMessage": {
"$ref": "#/components/messages/HandleOrdersMessage"
}
}
}
},
"operations": {
"receiveHandleOrders": {
"action": "receive",
"channel": {
"$ref": "#/channels/HandleOrders"
}
}
},
"components": {
"messages": {
"HandleOrdersMessage": {
"headers": {
"$ref": "#/components/schemas/HandleOrdersMessageHeaders"
}
}
},
"schemas": {
"HandleOrdersMessageHeaders": {
"properties": {
"request-id": {
"title": "Request-Id",
"type": "string"
}
},
"required": [
"request-id"
],
"title": "HandleOrdersMessageHeaders",
"type": "object"
}
}
}
}
Dependencies Use The Same Inputs¶
Dependency functions can declare the same kinds of parameters as a channel handler:
Payload(the message body)Headervalueschannel parameters (from the address template)
bindings (via protocol binding types)
MessageSenderfor sending follow-up messages
These inputs are resolved for dependencies exactly the same way they are for handlers.
Sub-dependencies¶
Dependencies can depend on other dependencies using the same Depends pattern:
from typing import Annotated
from asyncfast import AsyncFast
from asyncfast import Depends
from asyncfast import Header
app = AsyncFast()
def get_tenant_id(tenant_id: Annotated[str, Header(alias="tenant-id")]) -> str:
return tenant_id
def get_context(
tenant_id: Annotated[str, Depends(get_tenant_id)],
) -> dict[str, str]:
return {"tenant_id": tenant_id}
@app.channel("billing")
async def handle_billing(
context: Annotated[dict[str, str], Depends(get_context)],
) -> None:
print(context["tenant_id"])
{
"asyncapi": "3.0.0",
"info": {
"title": "AsyncFast",
"version": "0.1.0"
},
"channels": {
"HandleBilling": {
"address": "billing",
"messages": {
"HandleBillingMessage": {
"$ref": "#/components/messages/HandleBillingMessage"
}
}
}
},
"operations": {
"receiveHandleBilling": {
"action": "receive",
"channel": {
"$ref": "#/channels/HandleBilling"
}
}
},
"components": {
"messages": {
"HandleBillingMessage": {
"headers": {
"$ref": "#/components/schemas/HandleBillingMessageHeaders"
}
}
},
"schemas": {
"HandleBillingMessageHeaders": {
"properties": {
"tenant-id": {
"title": "Tenant-Id",
"type": "string"
}
},
"required": [
"tenant-id"
],
"title": "HandleBillingMessageHeaders",
"type": "object"
}
}
}
}
Cleanup With Yield¶
If a dependency is a generator, AsyncFast treats it like a context manager and runs cleanup after the handler returns.
This works for both def generators and async def generators:
from collections.abc import AsyncGenerator
from typing import Annotated
from asyncfast import AsyncFast
from asyncfast import Depends
app = AsyncFast()
async def get_resource() -> AsyncGenerator[str, None]:
resource = "connected"
try:
yield resource
finally:
# Cleanup happens after the handler returns.
pass
@app.channel("ping")
async def handle_ping(
resource: Annotated[str, Depends(get_resource)],
) -> None:
print(resource)
{
"asyncapi": "3.0.0",
"info": {
"title": "AsyncFast",
"version": "0.1.0"
},
"channels": {
"HandlePing": {
"address": "ping",
"messages": {
"HandlePingMessage": {
"$ref": "#/components/messages/HandlePingMessage"
}
}
}
},
"operations": {
"receiveHandlePing": {
"action": "receive",
"channel": {
"$ref": "#/channels/HandlePing"
}
}
},
"components": {
"messages": {
"HandlePingMessage": {}
}
}
}
Caching¶
By default, a dependency result is cached per message and reused if requested multiple times. To disable caching, set
use_cache=False on Depends:
from typing import Annotated
from asyncfast import AsyncFast
from asyncfast import Depends
from asyncfast import Header
app = AsyncFast()
def get_request_id(request_id: Annotated[str, Header(alias="request-id")]) -> str:
return request_id
@app.channel("events")
async def handle_events(
request_id: Annotated[str, Depends(get_request_id, use_cache=False)],
request_id_again: Annotated[str, Depends(get_request_id, use_cache=False)],
) -> None:
print(request_id, request_id_again)
{
"asyncapi": "3.0.0",
"info": {
"title": "AsyncFast",
"version": "0.1.0"
},
"channels": {
"HandleEvents": {
"address": "events",
"messages": {
"HandleEventsMessage": {
"$ref": "#/components/messages/HandleEventsMessage"
}
}
}
},
"operations": {
"receiveHandleEvents": {
"action": "receive",
"channel": {
"$ref": "#/channels/HandleEvents"
}
}
},
"components": {
"messages": {
"HandleEventsMessage": {
"headers": {
"$ref": "#/components/schemas/HandleEventsMessageHeaders"
}
}
},
"schemas": {
"HandleEventsMessageHeaders": {
"properties": {
"request-id": {
"title": "Request-Id",
"type": "string"
}
},
"required": [
"request-id"
],
"title": "HandleEventsMessageHeaders",
"type": "object"
}
}
}
}