Handler Mixins
Chanx supports composing reusable handler logic via Python mixin classes. A mixin is a plain class that defines @ws_handler or @event_handler methods — when mixed into a consumer through multiple inheritance, its handlers are automatically discovered and available for message routing and AsyncAPI documentation.
WebSocket Handler Mixin
A WebSocket handler mixin defines @ws_handler methods that handle incoming client messages:
from typing import Literal
from chanx.core.decorators import ws_handler
from chanx.messages.base import BaseMessage
# Define message types for the mixin
class ExtraRequestMessage(BaseMessage):
action: Literal["extra_request"] = "extra_request"
payload: str
class ExtraResponseMessage(BaseMessage):
action: Literal["extra_response"] = "extra_response"
payload: str
# Define the mixin
class ExtraWsHandlerMixin:
@ws_handler(
summary="Handle extra request",
description="Simple extra message",
)
async def handle_extra_message(
self, message: ExtraRequestMessage
) -> ExtraResponseMessage:
return ExtraResponseMessage(payload=message.payload + " any extra thing")
Compose the mixin into a consumer using multiple inheritance:
class ChatConsumer(ExtraWsHandlerMixin, AsyncJsonWebsocketConsumer):
@ws_handler(summary="Handle ping")
async def handle_ping(self, _message: PingMessage) -> PongMessage:
return PongMessage()
ChatConsumer now handles both ping and extra_request messages.
Event Handler Mixin
An event handler mixin defines @event_handler methods that handle channel layer events:
from chanx.core.decorators import event_handler
from chanx.messages.base import BaseMessage
class ExtraEventMessage(BaseMessage):
action: Literal["extra_event"] = "extra_event"
payload: str
class ExtraEventHandlerMixin:
@event_handler(
summary="Handle extra event",
description="Simple extra event message",
)
async def handle_extra_event(
self, event: ExtraEventMessage
) -> ExtraResponseMessage:
return ExtraResponseMessage(payload=event.payload + " any extra thing")
Compose into a consumer:
class BackgroundJobConsumer(ExtraEventHandlerMixin, AsyncJsonWebsocketConsumer):
@ws_handler(summary="Handle job requests")
async def handle_job(self, message: JobMessage) -> None:
...
Combining Multiple Mixins
A single consumer can use multiple mixins:
class FullFeaturedConsumer(
ExtraWsHandlerMixin,
ExtraEventHandlerMixin,
AsyncJsonWebsocketConsumer,
):
@ws_handler(summary="Handle ping")
async def handle_ping(self, _message: PingMessage) -> PongMessage:
return PongMessage()
This consumer handles ping, extra_request (from the WebSocket mixin), and extra_event (from the event mixin).
Contributing Groups
A mixin whose @event_handler methods rely on a channel group needs that group to be joined, but groups is owned by the underlying framework and follows normal MRO shadowing — if two classes in the chain declare it, only the most derived one applies. Use extra_groups instead, which Chanx merges across the whole MRO:
class HealthMixin:
extra_groups: ClassVar[list[str]] = ["health_group"]
@event_handler
async def handle_health_alert(
self, event: HealthAlertEvent
) -> HealthAlertEvent:
return event
class EchoMixin:
extra_groups: ClassVar[list[str]] = ["echo_group"]
class GatewayConsumer(HealthMixin, EchoMixin, AsyncJsonWebsocketConsumer):
groups = ["static_group"]
GatewayConsumer joins static_group, health_group, and echo_group. Duplicates are collapsed, and groups keeps its usual behaviour, so a subclass can still replace an inherited group list outright.
How It Works
Mixin handlers are auto-discovered through Python's method resolution order (MRO). When a consumer class is defined, Chanx scans all methods in the inheritance chain for @ws_handler and @event_handler decorators and registers them in the consumer's handler maps. This means:
No extra registration — just add the mixin to your class's bases
Message routing works automatically — mixin handlers are routed the same way as handlers defined directly on the consumer
AsyncAPI docs include mixin handlers — generated documentation reflects all handlers, including those from mixins