Django Channels Router (Backend, PyPI)
A lightweight Django + Channels router that lets your WebSocket consumers behave like HTTP endpoints.
Designed to pair seamlessly with @djanext/observable-socket.
Features
- 🧭 Route names → handler methods (
sayHello→on_say_hello) - ⚡ Both sync & async consumers
- 🧩 Optional
hydrate/dehydratefunctions - 🔁 Built-in heartbeat support (
PING/PONG) - 📦 Typed results and HTTP-style status codes
Requirements
- Python ≥ 3.11
- Django ≥ 4.2
- Channels ≥ 4.0
- Redis (or any supported channel layer)
Installation
pip install django-channels-router
In your project:
# routing.py
from django.urls import re_path
from .consumer import AppSocket
websocket_urlpatterns = [
re_path(r"ws/app/$", AppSocket.as_asgi()),
]
Example Consumer
from django_channels_router import SocketRouterConsumer, StatusCodes
def can_load_article(scope, fetched_data):
class AppSocket(SocketRouterConsumer):
@SocketRouterConsumer.routes.setter
def routes(self, _):
self._routes = [
{"route": "sayHello"},
{"route": "getArticle",
""
"hydrate": lambda headers, p: load_article(p["id"]),
"dehydrate": lambda art: {"id": art.id, "title": art.title}
},
]
def on_say_hello(self, payload, headers):
name = (payload or {}).get("name", "World")
return {"status": StatusCodes.OK, "payload": f"Hello, {name}!"}
Async Example
from django_channels_router import AsyncSocketRouterConsumer
class AppSocketAsync(AsyncSocketRouterConsumer):
@AsyncSocketRouterConsumer.routes.setter
def routes(self, _):
self._routes = [{"route": "sayHello"}]
async def on_say_hello(self, payload, headers):
return {"status": 200, "payload": {"msg": "Hello async!"}}
Serializing and Deserializing of messages
hydrate and dehydrate functions helps to focus only on logic part as these functions will handle deserializing and serializing automatically. To use them, first you need to implement the converters (loader, serializer, ...)
def load_article(headers, payload) -> Article | None:
try:
article_id = headers.get('id')
if not article_id:
return None
return Article.objects.get(id=article_id) # assume id is of type string like uuid
except Article.DoesNotExist:
return None
def deserialize(headers, payload) -> Article | None:
try:
serializer = ArticleSerializer(data=payload)
serializer.is_valid(raise_exception=True)
return serializer.save()
except serializers.ValidationError:
return None
def serialize(article: Article) -> str:
serializer = ArticleSerializer(article)
return serializer.data
now these converters can be used as follows:
class ArticleConsumer(SocketRouterConsumer):
routes = [
{route: get, hydrate: load_article, dehydrate: serialize},
{route: create, hydrate: deserialize, dehydrate: serialize}
]
def on_get(headers, payload):
return {
payload: payload,
# article gets fetched from DB in background using the `load_article` function
# dehydrate will automatically serilize the article by running `serialize` function
status: StatusCodes.OK if payload else StatusCodes.NOT_FOUND
}
def on_create(headers, payload):
if not payload: # deserialize function has returned None
return {
payload: "Unable to create article",
status: StatusCodes.BAD_REQUEST
}
return {
payload: payload,
status: StatusCodes.CREATED
}
Note:
dehydrate function only applies if returned status code by user is 200 series. (199 < status code < 300)
Status Codes
| Symbol | Value | Meaning |
|---|---|---|
StatusCodes.OK |
200 | Success |
StatusCodes.BAD_REQUEST |
400 | Malformed message |
StatusCodes.NOT_FOUND |
404 | Unknown route |
StatusCodes.INTERNAL_SERVER_ERROR |
500 | Handler failure |
Good Practices
- Always
accept()connections inconnect()/await accept(). - Access
result.get("payload")safely — avoid missing keys. - Use
@classmethodroute definitions if you plan to reuse the router base. - Keep payload JSON-serializable.
Frontend Client
Pair with @djanext/observable-socket
→ Handles auto-reconnect, multiple sockets, and sendAndWait() with Promises.
License
MIT
Metadata
Release files for django-observable-socket 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| django_observable_socket-1.0.1.tar.gz | 19.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_observable_socket-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.3 kB
Release files / django_observable_socket-1.0.1.tar.gz
| Download URL | django_observable_socket-1.0.1.tar.gz |
|---|---|
| Size | 19.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
133ea59a613100e6239ec218b3a98077b0bc7081886e9c4ea6715e79652a4519
|
|
BLAKE2b-256 checksum How to use checksums |
697824b5477aab8d409cadff19c28c88e0e47dd7e2219e4e177f78ce3c504f4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / django_observable_socket-1.0.1-py3-none-any.whl
| Download URL | django_observable_socket-1.0.1-py3-none-any.whl |
|---|---|
| Size | 14.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8e481bf8be61057b0feafe33ff6d072a6d77ce44afc8ae0594c3b515541ed897
|
|
BLAKE2b-256 checksum How to use checksums |
17be4121c22ac260885eb03ce1c81e97a05e357bcc328d3edead22c2dfd93f9b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Security for django-observable-socket 1.0.1
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.