/blog/real-time-django-building-a-chat-app-with-websockets/ - zsh
user@portfolio ~ $

cat real-time-django-building-a-chat-app-with-websockets.md

Real-Time Django: Building a Chat App with WebSockets

Author: Aslany Rahim Published: December 08, 2025
Standard HTTP is strictly Request-Response. To build a real-time chat or notification system, you need persistent connections. Learn how to upgrade Django from WSGI to ASGI using Django Channels.

Django was born in the era of "Request -> Response". You click a link, the server sends a page, and the connection closes. But modern apps need Real-Time features: Chat messages popping up instantly, notification bells ringing, or live graphs updating.

For this, we need WebSockets, and in Django, we use Django Channels.

WSGI vs. ASGI

Standard Django runs on WSGI (Web Server Gateway Interface), which is synchronous (blocking). To handle hundreds of open WebSocket connections simultaneously, we need ASGI (Asynchronous Server Gateway Interface).

Step 1: Installation

We need channels and daphne (the ASGI interface server).

pip install channels daphne channels-redis

Update settings.py:

INSTALLED_APPS = [
    'daphne', # Must be at the top
    'django.contrib.admin',
    # ...
    'channels',
]

ASGI_APPLICATION = 'myproject.asgi.application'

# We need Redis to act as the "Channel Layer" (passing messages between users)
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}

Step 2: Consumers

In classic Django, we have Views. In Channels, we have Consumers. A consumer handles the WebSocket lifecycle: Connect, Receive, Disconnect.

Create chat/consumers.py:

import json
from channels.generic.websocket import AsyncWebsocketConsumer

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = "global_chat"
        self.room_group_name = f"chat_{self.room_name}"

        # Join room group via Redis
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket (Frontend)
    async def receive(self, text_data):
        data = json.loads(text_data)
        message = data['message']

        # Send message to room group (Redis)
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event['message']
        # Send message to WebSocket (Frontend)
        await self.send(text_data=json.dumps({
            'message': message
        }))

Step 3: Routing

We need a URL router specifically for WebSockets.

In chat/routing.py:

from django.urls import re_path
from . import consumers

websocket_urlpatterns = [
    re_path(r'ws/chat/$', consumers.ChatConsumer.as_asgi()),
]

Step 4: Frontend

The JavaScript is surprisingly simple.

const chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/');

chatSocket.onmessage = function(e) {
    const data = JSON.parse(e.data);
    console.log("New Message:", data.message);
};

chatSocket.onclose = function(e) {
    console.error('Chat socket closed unexpectedly');
};

// Sending a message
document.querySelector('#send-btn').onclick = function(e) {
    chatSocket.send(JSON.stringify({
        'message': 'Hello World!'
    }));
};

Conclusion

With Django Channels, you don't need to switch to Node.js to get real-time capabilities. You keep your ORM, your authentication, and your project structure, but gain the power of persistent connections.

35 views
0 comments

Comments (0)

Leave a Comment

No comments yet. Be the first to comment!

Related Posts

Level Up Your Django Tests: Switching from Unittest to Pytest

Standard Django testing is verbose. Discover why the Python community is moving to Pytest, how to use Fixtures effectively, and …

December 06, 2025

REST vs. GraphQL: Is it Time to Switch in Django?

Tired of hitting three different API endpoints just to render one profile page? We compare the traditional REST approach with …

December 03, 2025

Intercepting Requests: A Guide to Custom Django Middleware

Middleware is the "Onion" of Django. Learn how to write custom hooks that process every request entering your application and …

December 02, 2025

user@portfolio ~ $ _