Skip to main content

CHANX (CHANnels-eXtension)

PyPI Code Coverage Test Checked with mypy Checked with pyright Interrogate Badge

A batteries-included WebSocket framework for Django Channels, FastAPI, and ASGI-based applications. Chanx provides automatic message routing, Pydantic validation, type safety, AsyncAPI documentation generation, and comprehensive testing utilities out of the box.

Why Use Chanx?

Without Chanx - Manual routing, validation, and documentation:

# Django Channels - manual routing
async def receive(self, text_data):
    data = json.loads(text_data)
    action = data.get("action")

    if action == "chat":
        if "message" not in data.get("payload", {}):
            await self.send(json.dumps({"error": "Missing message"}))
            return
        # Handle chat...
    elif action == "ping":
        await self.send(json.dumps({"action": "pong"}))
    # ... endless if-else chains

# FastAPI - manual routing, no broadcasting, no groups
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_json()
        action = data.get("action")

        if action == "chat":
            if "message" not in data.get("payload", {}):
                await websocket.send_json({"error": "Missing message"})
                continue
            # No broadcasting, must manually track connections
            # No type safety, no validation, no documentation
        elif action == "ping":
            await websocket.send_json({"action": "pong"})
        # ... more manual handling

With Chanx - Automatic routing, validation, and type safety:

@ws_handler(output_type=ChatNotificationMessage)
async def handle_chat(self, message: ChatMessage) -> None:
    # Automatically routed, validated, and type-safe
    await self.broadcast_message(
        ChatNotificationMessage(payload=message.payload)
    )

@ws_handler
async def handle_ping(self, message: PingMessage) -> PongMessage:
    return PongMessage()  # Auto-documented in AsyncAPI

What Chanx Eliminates:

Technical Pain Points:

  • Manual if-else routing chains → Automatic routing via decorators + Pydantic discriminated unions

  • Manual validation code → Pydantic validate_python() with type-safe models

  • Runtime type surprises → Catch errors during development with mypy/pyright static checking

  • Writing API docs → Auto-generated AsyncAPI 3.0 specs

  • Framework lock-in → Single codebase works with both Django Channels and FastAPI

  • Testing complexity → Comprehensive testing utilities

Team Collaboration Nightmares:

  • Inconsistent implementations → Enforced patterns via decorators and type-safe messages

  • Painful code reviews → Clean, declarative handlers instead of nested if-else chains

  • Slow onboarding → Self-documenting code with AsyncAPI specs as single source of truth

  • No API contract → Auto-generated AsyncAPI documentation and typed clients for frontend teams

  • Fragile tests → Built-in testing utilities with standardized patterns

  • Debugging hell → Structured logging with automatic request/response tracing

Built on years of real-world WebSocket development experience, Chanx provides proven patterns that help teams ship faster, maintain cleaner code, and reduce debugging time.

Installation

For Django Channels Projects

pip install "chanx[channels]"

For FastAPI and Other ASGI Frameworks

pip install "chanx[fast_channels]"

For Client Generator CLI

pip install "chanx[cli]"

For Using Generated Clients

pip install "chanx[client]"

Prerequisites

For Django: Ensure Django Channels is properly set up. See Django Channels documentation.

For FastAPI: Ensure fast-channels is properly set up. See fast-channels documentation.

Quick Start

1. Define Message Types with Discriminated Action Field

Create message types using Pydantic with a Literal action field for automatic routing:

from typing import Literal
from pydantic import BaseModel
from chanx.messages.base import BaseMessage

# Define message payloads
class ChatPayload(BaseModel):
    message: str

# Incoming message from client
class ChatMessage(BaseMessage):
    action: Literal["chat"] = "chat"
    payload: ChatPayload

# Outgoing notification to clients
class ChatNotificationMessage(BaseMessage):
    action: Literal["chat_notification"] = "chat_notification"
    payload: ChatPayload

2. Create WebSocket Consumer

Use decorators to define handlers that automatically route and validate messages:

from chanx.core.decorators import ws_handler, channel

# For Django
from chanx.channels.websocket import AsyncJsonWebsocketConsumer

# For FastAPI
# from chanx.fast_channels.websocket import AsyncJsonWebsocketConsumer

@channel(name="chat", description="Real-time chat API")
class ChatConsumer(AsyncJsonWebsocketConsumer):
    groups = ["chat_room"]  # Auto-join this group on connect

    @ws_handler(
        summary="Handle chat messages",
        output_type=ChatNotificationMessage
    )
    async def handle_chat(self, message: ChatMessage) -> None:
        # Broadcast to all clients in the group
        await self.broadcast_message(
            ChatNotificationMessage(
                payload=ChatPayload(message=f"User: {message.payload.message}")
            )
        )

3. Setup Routing

For Django:

# yourapp/routing.py
from chanx.channels.routing import path
from channels.routing import URLRouter
from .consumers import ChatConsumer

router = URLRouter([
    path("chat/", ChatConsumer.as_asgi()),
])

# config/asgi.py
from channels.routing import ProtocolTypeRouter
from chanx.channels.routing import include
from django.core.asgi import get_asgi_application

django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": include("yourapp.routing"),
})

For FastAPI:

# main.py
from fastapi import FastAPI
from .consumers import ChatConsumer

app = FastAPI()

# Create WebSocket sub-app
ws_router = FastAPI()
ws_router.add_websocket_route("/chat", ChatConsumer.as_asgi())

# Mount WebSocket routes
app.mount("/ws", ws_router)

4. Run Server

Django (with Daphne or Uvicorn):

# Using Daphne
daphne config.asgi:application

# Or using Uvicorn
uvicorn config.asgi:application

FastAPI:

uvicorn main:app

5. Client Usage

Connect from JavaScript and send/receive typed messages:

const ws = new WebSocket('ws://localhost:8000/ws/chat')

// Send message - automatically validated and routed
ws.send(JSON.stringify({
    "action": "chat",
    "payload": {"message": "Hello everyone!"}
}))

// Receive broadcast
ws.onmessage = (event) => {
    const data = JSON.parse(event.data)
    // {"action": "chat_notification", "payload": {"message": "User: Hello everyone!"}}
    console.log(data.payload.message)
}

6. Add AsyncAPI Documentation

For Django:

# config/urls.py (your main urls.py)
from django.urls import path, include

urlpatterns = [
    # ... other patterns
    path("asyncapi/", include("chanx.channels.urls")),
]

For FastAPI:

from chanx.fast_channels import asyncapi_docs, asyncapi_spec_json
from chanx.fast_channels.type_defs import AsyncAPIConfig

config = AsyncAPIConfig(
    description="WebSocket API documentation",
    version="1.0.0"
)

@app.get("/asyncapi")
async def docs(request: Request):
    return await asyncapi_docs(request=request, app=app, config=config)

@app.get("/asyncapi.json")
async def spec(request: Request):
    return await asyncapi_spec_json(request=request, app=app, config=config)

Visit /asyncapi/ to see your auto-generated interactive documentation.

Configuration

Django - Configure via settings.py:

# settings.py
CHANX = {
    # Message handling
    'MESSAGE_ACTION_KEY': 'action',  # Discriminator field name
    'CAMELIZE': False,  # Convert snake_case to camelCase for JS clients
    'SEND_COMPLETION': False,  # Send completion message after handling
    'SEND_MESSAGE_IMMEDIATELY': True,  # Yield control after sending
    'SEND_AUTHENTICATION_MESSAGE': True,  # Send auth status after connect

    # Logging
    'LOG_WEBSOCKET_MESSAGE': True,  # Log WebSocket messages
    'LOG_IGNORED_ACTIONS': [],  # Actions to exclude from logging

    # WebSocket
    'WEBSOCKET_BASE_URL': None,  # Override WebSocket URL

    # AsyncAPI documentation
    'ASYNCAPI_TITLE': 'AsyncAPI Documentation',
    'ASYNCAPI_DESCRIPTION': '',
    'ASYNCAPI_VERSION': '1.0.0',
    'ASYNCAPI_SERVER_URL': None,
    'ASYNCAPI_SERVER_PROTOCOL': None,
}

FastAPI - Configure via class attributes (can also be used per-consumer in Django):

from chanx.fast_channels.websocket import AsyncJsonWebsocketConsumer

class BaseConsumer(AsyncJsonWebsocketConsumer):
    # Message handling
    camelize = False
    discriminator_field = "action"
    send_completion = False
    send_message_immediately = True

    # Logging
    log_websocket_message = False
    log_ignored_actions = []

    # Channel layer (FastAPI)
    channel_layer_alias = "default"

Per-Consumer Override (Django):

@channel(name="chat")
class ChatConsumer(AsyncJsonWebsocketConsumer):
    # Override global settings for this consumer
    send_completion = True
    log_ignored_actions = ["ping", "pong"]

Key Features

Decorator-Based Handlers

@ws_handler for WebSocket messages, @event_handler for channel events, @channel for consumer metadata

Handler Mixins

Compose reusable @ws_handler and @event_handler logic via Python mixin classes

Discriminated Union Routing

Automatic message type detection and routing using Pydantic’s discriminator field pattern

AsyncAPI 3.0 Generation

Auto-generate interactive documentation and OpenAPI-style specs from decorated handlers

Type-Safe Clients

Generate Python clients with the built-in generator, and JavaScript/TypeScript clients with React, Vue, Svelte and Solid bindings via chanx-js

Authentication System

Built-in DjangoAuthenticator with DRF permission support, extensible BaseAuthenticator for custom flows

Channel Layer Integration

Type-safe broadcast_message(), send_event(), and broadcast_event() with full validation

Testing Utilities

Framework-specific WebsocketCommunicator wrappers and test helpers for end-to-end WebSocket testing

Structured Logging

Automatic request/response logging with structlog, configurable action filtering, error tracing

Configuration Management

Django settings integration via CHANX dict, class-level config for FastAPI consumers

Clients

Clients are generated from the AsyncAPI schema, so they always match the server.

Python, with the built-in generator:

# Generate from local file (JSON or YAML)
chanx generate-client --schema asyncapi.json --output ./my_client
chanx generate-client --schema asyncapi.yaml --output ./my_client

# Generate directly from URL (no need to download)
chanx generate-client --schema http://localhost:8000/asyncapi.json --output ./my_client
# Use generated client with full type safety
from my_client.chat import ChatClient, ChatMessage, ChatPayload

client = ChatClient("localhost:8000")

await client.send_message(
    ChatMessage(payload=ChatPayload(message="Hello!"))
)

JavaScript and TypeScript, with chanx-js:

pnpm add @chanx-js/client
pnpm add -D @chanx-js/codegen
npx @chanx-js/codegen http://localhost:8000/asyncapi.json -o src/generated
import { useChannel } from '@chanx-js/client/react';
import { chat } from './generated';

function Chat() {
  const { lastMessage, send } = useChannel(chat);

  // Narrowed by `action`, so the payload is ChatNotificationMessage's.
  const latest =
    lastMessage?.action === 'chat_notification' ? lastMessage.payload.message : '';

  // `send` only accepts messages the server declares, such as ChatMessage.
  return (
    <button onClick={() => send({ action: 'chat', payload: { message: 'Hello!' } })}>
      {latest || 'Say hello'}
    </button>
  );
}

It also covers topics, requests, reconnection, and Vue, Svelte, Solid or plain JS.

Learn More

Metadata

Release files for chanx 2.11.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for chanx 2.11.3
File Size Uploaded
chanx-2.11.3.tar.gz 88.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for chanx 2.11.3
File Interpreter ABI Platform
chanx-2.11.3-py3-none-any.whl Python 3 none any Details

Total release size: 202.9 kB

Release files / chanx-2.11.3.tar.gz

Download URL chanx-2.11.3.tar.gz
Size 88.3 kB
Tags Source
SHA-256 checksum
How to use checksums
8e30331f2903d65861655e5173adefe73ecebf1c23e834902de9d424b80aed11
BLAKE2b-256 checksum
How to use checksums
1c76d663f699c1549db55999d78fab32a7890a171ff153731b016db9772a3825
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / chanx-2.11.3-py3-none-any.whl

Download URL chanx-2.11.3-py3-none-any.whl
Size 114.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c27fcdc1ac05003194c7b34d217789d7e51cc55a6011426f81ba9a309c17c672
BLAKE2b-256 checksum
How to use checksums
c33786a55a7b17f46a022c3557f917c3e15a067ceb8d1a22fee1bbeed6d12f31
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

2.11.4

2 release files

This release

2.11.3 This release

2 release files

2.11.2

2 release files

2.11.1

2 release files

2.11.0

2 release files

2.10.2

2 release files

2.10.0

2 release files

2.9.0

2 release files

2.8.1

2 release files

2.8.0

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.6.2

2 release files

2.6.1

2 release files

2.6.0

2 release files

2.5.0

2 release files

2.4.1

2 release files

2.4.0

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.2

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.13.3

2 release files

0.13.2

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page