Skip to content

Commit ea94967

Browse files
committed
fix(docs): add AsyncAPI mapping explanations and improve navigation
1 parent ab0be22 commit ea94967

File tree

10 files changed

+368
-39
lines changed

10 files changed

+368
-39
lines changed

docs/examples/fastapi.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,18 +574,22 @@ Production Deployment
574574
Key considerations for production deployment:
575575

576576
**1. Channel Layer Scaling**
577+
577578
- Use Redis Cluster or RabbitMQ for high-availability channel layers
578579
- Configure appropriate connection pools and timeouts
579580

580581
**2. Background Job Processing**
582+
581583
- Deploy ARQ workers as separate processes/containers
582584
- Use Redis Sentinel for worker queue high availability
583585

584586
**3. WebSocket Load Balancing**
587+
585588
- Configure sticky sessions or use Redis for session storage
586589
- Consider using a WebSocket-aware load balancer
587590

588591
**4. Monitoring and Observability**
592+
589593
- Enable structured logging with correlation IDs
590594
- Monitor WebSocket connection counts and message rates
591595
- Set up health checks for both HTTP and WebSocket endpoints

docs/quick-start-django.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@ Next Steps
265265

266266
Now that you have a working Django WebSocket consumer with Chanx:
267267

268+
**Tutorial:**
269+
270+
* :doc:`tutorial-django/prerequisites` - **Follow the comprehensive Django tutorial** to build a complete real-time application with chat, AI assistants, background tasks, and testing
271+
272+
**Documentation:**
273+
268274
* :doc:`user-guide/consumers-decorators` - Learn more about consumers and decorators
269275
* :doc:`user-guide/framework-integration` - Explore Django-specific features
270276
* :doc:`user-guide/asyncapi` - Learn about AsyncAPI documentation generation

docs/quick-start-fastapi.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@ Next Steps
271271

272272
Now that you have a working FastAPI WebSocket consumer with Chanx:
273273

274+
**Tutorial:**
275+
276+
* :doc:`tutorial-fastapi/prerequisites` - **Follow the comprehensive FastAPI tutorial** to build a complete real-time application with system echo, room chat, background jobs, and multi-layer messaging
277+
278+
**Documentation:**
279+
274280
* :doc:`user-guide/consumers-decorators` - Learn more about consumers and decorators
275281
* :doc:`user-guide/framework-integration` - Explore FastAPI-specific features
276282
* :doc:`user-guide/asyncapi` - Learn about AsyncAPI documentation generation

docs/tutorial-django/cp1-chat-websocket.rst

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,51 @@ This is the core chat functionality:
169169
- ``broadcast_message()`` sends the message to all clients in the same group
170170
- ``exclude_current=True`` means the sender won't receive their own message back
171171

172+
How Message Handlers Send Messages
173+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
174+
175+
Understanding how messages are sent back to clients is important.
176+
177+
**Pattern 1: Return value sends to sender only**
178+
179+
.. code-block:: python
180+
181+
@ws_handler
182+
async def handle_ping(self, message: PingMessage) -> PongMessage:
183+
# What you return goes back to the sender only
184+
return PongMessage()
185+
186+
The ``handle_ping`` method demonstrates this - it returns ``PongMessage`` which is automatically sent to the client who sent the ping.
187+
188+
**Pattern 2: Broadcasting to multiple users**
189+
190+
.. code-block:: python
191+
192+
@ws_handler(output_type=NewChatMessage)
193+
async def handle_new_chat_message(self, message: NewChatMessage) -> None:
194+
# Explicitly broadcast to send to multiple users
195+
await self.broadcast_message(message, exclude_current=True)
196+
197+
The ``handle_new_chat_message`` method demonstrates this:
198+
199+
- Return type is ``None`` (not sending directly to sender)
200+
- Use ``output_type`` parameter in ``@ws_handler`` for API documentation
201+
- Call ``broadcast_message()`` explicitly to send to all group members
202+
- ``exclude_current=True`` means the sender won't receive their own message
203+
204+
.. note::
205+
206+
You'll see more advanced messaging patterns, including server-to-server communication with event handlers, in Part 4.
207+
208+
AsyncAPI Documentation Mapping
209+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
210+
211+
The ``@ws_handler`` decorator generates AsyncAPI **RECEIVE** actions (documenting what messages clients can send). When handlers have a return type or ``output_type`` parameter, the RECEIVE action includes a **reply field** describing the response message.
212+
213+
.. seealso::
214+
215+
For detailed information about AsyncAPI mapping, see :doc:`../user-guide/consumers-decorators` → AsyncAPI Documentation Mapping section.
216+
172217
Step 3: Create WebSocket Routing
173218
---------------------------------
174219

docs/tutorial-django/cp3-system-websocket.rst

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,102 @@ The ``@event_handler`` decorator marks this method as a channel layer event hand
463463

464464
Similarly for ``handle_system_notification`` - handles broadcast notifications.
465465

466+
Understanding Event Handlers
467+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
468+
469+
This is the first time we're using ``@event_handler``, so let's understand what it does and how it differs from ``@ws_handler``.
470+
471+
**@ws_handler vs @event_handler:**
472+
473+
.. code-block:: python
474+
475+
# Receives messages from WebSocket clients
476+
@ws_handler
477+
async def handle_user_message(self, message: UserMessage) -> Response:
478+
...
479+
480+
# Receives messages from channel layer (server-to-server)
481+
@event_handler
482+
async def handle_job_result(self, event: JobResult) -> Response:
483+
...
484+
485+
**Key differences:**
486+
487+
+------------------+------------------------------------+--------------------------------------------+
488+
| | @ws_handler | @event_handler |
489+
+==================+====================================+============================================+
490+
| **Source** | WebSocket clients | Channel layer (server-side) |
491+
+------------------+------------------------------------+--------------------------------------------+
492+
| **Triggered by** | Client sends JSON message | ``send_event()`` or ``broadcast_event()`` |
493+
+------------------+------------------------------------+--------------------------------------------+
494+
| **Use case** | Handle user interactions | Handle background job results, external |
495+
| | | triggers, cross-consumer messages |
496+
+------------------+------------------------------------+--------------------------------------------+
497+
498+
**How event handlers work:**
499+
500+
**Pattern 1: Return value sends to WebSocket client**
501+
502+
.. code-block:: python
503+
504+
@event_handler
505+
async def handle_job_result(self, event: JobResult) -> JobResult:
506+
# What you return is sent to the WebSocket client
507+
return event
508+
509+
Where the message goes depends on how the event was sent:
510+
511+
- ``send_event(message, channel_name)`` → goes to **one specific client**
512+
- ``broadcast_event(message, groups=[...])`` → goes to **all clients in those groups**
513+
514+
In our system example:
515+
516+
.. code-block:: python
517+
518+
# Celery task sends to specific client
519+
SystemConsumer.send_event_sync(JobResult(payload=result), channel_name)
520+
521+
# Event handler receives it and forwards to that client
522+
@event_handler
523+
async def handle_job_result(self, event: JobResult) -> JobResult:
524+
return event
525+
526+
**Pattern 2: Send multiple messages or complex logic**
527+
528+
.. code-block:: python
529+
530+
@event_handler(output_type=Notification)
531+
async def handle_complex_event(self, event: ComplexEvent) -> None:
532+
# Send multiple messages
533+
await self.send_message(Notification(payload="Processing..."))
534+
await self.send_message(Notification(payload="Complete!"))
535+
536+
When using complex logic:
537+
538+
- Return type is ``None``
539+
- Use ``output_type`` parameter for API documentation
540+
- Call ``send_message()`` or ``broadcast_message()`` explicitly
541+
542+
**Why use event handlers?**
543+
544+
Event handlers enable server-to-server communication:
545+
546+
- **Background workers** (Celery) can send results back to WebSocket clients
547+
- **External scripts** can trigger WebSocket notifications
548+
- **HTTP endpoints** can push messages to WebSocket connections
549+
- **Different consumers** can send messages to each other
550+
551+
This is more powerful than ``@ws_handler`` which only handles client messages.
552+
553+
AsyncAPI Documentation Mapping for Event Handlers
554+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
555+
556+
The ``@event_handler`` decorator generates AsyncAPI **SEND** actions (server-initiated messages). Only the output type (message sent to client) is documented. The input event type is NOT documented in AsyncAPI since it comes from internal sources (Celery, management commands, etc.), not from WebSocket clients.
557+
558+
.. seealso::
559+
560+
For detailed information about AsyncAPI mapping, see :doc:`../user-guide/consumers-decorators` → AsyncAPI Documentation Mapping section.
561+
466562
.. tip::
467563

468564
**Optional: Generic Type Parameter for Better Type Hints**
@@ -497,13 +593,6 @@ Similarly for ``handle_system_notification`` - handles broadcast notifications.
497593
498594
This catches bugs during development before runtime, providing better IDE autocomplete and static analysis. The generic type doesn't affect runtime behavior.
499595

500-
.. important::
501-
502-
**Key Difference:**
503-
504-
- ``@ws_handler`` - Handles messages from **WebSocket clients**
505-
- ``@event_handler`` - Handles messages from **channel layer** (server-to-server)
506-
507596
Step 6: Create Routing
508597
----------------------
509598

docs/tutorial-fastapi/cp1-system-echo.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ When broadcasting:
245245

246246
You'll see more advanced messaging patterns, including server-to-server communication with event handlers, in Part 3.
247247

248+
AsyncAPI Documentation Mapping
249+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
250+
251+
The ``@ws_handler`` decorator generates AsyncAPI **RECEIVE** actions (documenting what messages clients can send). When handlers have a return type or ``output_type`` parameter, the RECEIVE action includes a **reply field** describing the response message.
252+
253+
.. seealso::
254+
255+
For detailed information about AsyncAPI mapping, see :doc:`../user-guide/consumers-decorators` → AsyncAPI Documentation Mapping section.
256+
248257
Key Concepts Review
249258
-------------------
250259

docs/tutorial-fastapi/cp3-background-jobs.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,15 @@ Event handlers enable server-to-server communication:
513513

514514
This is more powerful than ``@ws_handler`` which only handles client messages.
515515

516+
AsyncAPI Documentation Mapping for Event Handlers
517+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
518+
519+
The ``@event_handler`` decorator generates AsyncAPI **SEND** actions (server-initiated messages). Only the output type (message sent to client) is documented. The input event type is NOT documented in AsyncAPI since it comes from internal sources (ARQ workers, HTTP endpoints, etc.), not from WebSocket clients.
520+
521+
.. seealso::
522+
523+
For detailed information about AsyncAPI mapping, see :doc:`../user-guide/consumers-decorators` → AsyncAPI Documentation Mapping section.
524+
516525
Step 4: Register the WebSocket Route
517526
-------------------------------------
518527

0 commit comments

Comments
 (0)