Skip to main content

Command Palette

Search for a command to run...

Real-Time Chat Feature with Django Channels, Redis & DRF

Updated
5 min read
Real-Time Chat Feature with Django Channels, Redis & DRF

Backend Workflow: Message Journey Through Django Channels

**Complete Flow Diagram**
=========================
USER SENDS MESSAGE
      ↓
[1] WebSocket sends: '{"message": "Hello"}'
      ↓
[2] routing.py → routes to ChatConsumer
      ↓
[3] consumers.py → receive() method
      ↓
      ├─→ [4] save_message()
      │         ↓
      │   Database INSERT
      │   (Message stored permanently)
      │
      └─→ [5] channel_layer.group_send()
                ↓
          [6] Channel Layer (InMemory/Redis)
                ↓
          Broadcasts to ALL consumers in group 'chat_1'
                ↓
          ┌──────────┬──────────┬──────────┐
          ↓          ↓          ↓          ↓
      Consumer A  Consumer B  Consumer C  Consumer D
          ↓          ↓          ↓          ↓
      [7] chat_message() method called on each
          ↓          ↓          ↓          ↓
      [8] send() to WebSocket
          ↓          ↓          ↓          ↓
      User A     User B     User C     User D
      receives   receives   receives   receives

Step 1: Install Required Packages

pip install channels channels-redis daphne redis

Step 2: Update Django Settings

INSTALLED_APPS = [
    'channels',
]

ASGI_APPLICATION = 'your_project.asgi.application'

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

Step 3: Configure ASGI (project/asgi.py)

import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')

django_asgi_app = get_asgi_application()

from chats.routing import websocket_urlpatterns

application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": AuthMiddlewareStack(URLRouter(websocket_urlpatterns)),
})

Step-4: Create Chat Models (app/models.py)

from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

class Room(models.Model):
    name = models.CharField(max_length=255, null=True, blank=True)
    participants = models.ManyToManyField(User, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

class Message(models.Model):
    room = models.ForeignKey(Room, on_delete=models.CASCADE, related_name='messages', null=True, blank=True)
    sender = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
    content = models.TextField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['created_at']

Step-5: Run Migrations

python manage.py makemigrations
python manage.py migrate

Step-6: Create WebSocket Consumer(app/consumers.py)

import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from .models import Room, Message

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_id = self.scope['url_route']['kwargs']['room_id']
        self.room_group = f'chat_{self.room_id}'

        await self.channel_layer.group_add(self.room_group, self.channel_name)
        await self.accept()

    async def disconnect(self, code):
        await self.channel_layer.group_discard(self.room_group, self.channel_name)

    async def receive(self, text_data):
        data = json.loads(text_data)
        message = data['message']
        user = self.scope['user']

        await self.save_message(user, message)

        username = user.username if user.is_authenticated else 'Anonymous'

        await self.channel_layer.group_send(
            self.room_group,
            {
                'type': 'chat_message',
                'message': message,
                'username': username,
            }
        )

    async def chat_message(self, event):
        await self.send(text_data=json.dumps({
            'message': event['message'],
            'username': event['username'],
        }))

    @database_sync_to_async
    def save_message(self, user, content):
        from django.contrib.auth import get_user_model
        User = get_user_model()

        room = Room.objects.get(id=self.room_id)

        if user.is_authenticated:
            actual_user = User.objects.get(id=user.id)
        else:
            return None

        return Message.objects.create(
            room=room,
            sender=actual_user,
            content=content
        )

Step-7: WebSocket Routing (chat/routing.py)

from django.urls import path
from . import consumers

websocket_urlpatterns = [
    path('ws/chat/<int:room_id>/', consumers.ChatConsumer.as_asgi()),
]

Step 8: REST API (chat/serializers.py)

from rest_framework import serializers 
from .models import Room, Message

class MessageSerializer(serializers.ModelSerializer):
    sender = serializers.StringRelatedField()

    class Meta:
        model = Message
        fields = ['id', 'sender', 'content', 'created_at']

class RoomSerializer(serializers.ModelSerializer):
    class Meta:
        model = Room
        fields = ['id', 'name', 'created_at']

Step 9: REST API Views (chat/views.py)

from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Room, Message
from .serializers import RoomSerializer, MessageSerializer

class RoomViewSet(viewsets.ModelViewSet):
    queryset = Room.objects.all()
    serializer_class = RoomSerializer

    @action(detail=True, methods=['get'])
    def messages(self, request, pk=None):
        room = self.get_object()
        messages = room.messages.all()
        serializer = MessageSerializer(messages, many=True)
        return Response(serializer.data)

Step 10: HTTP URLs (chat/urls.py)

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import RoomViewSet

router = DefaultRouter()
router.register('rooms', RoomViewSet)

urlpatterns = [
    path('', include(router.urls)),
]

Main urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/chat/', include('chat.urls')),
]

API Documentation

Base Url

HTTP: http://localhost:8000/api/chat/
WebSocket: ws://localhost:8000/ws/chat/

📡 REST API Endpoints

1. List All Chat Rooms

GET /api/chat/rooms/

Get all chat rooms.

Request:

GET /api/chat/rooms/
Content-Type: application/json

Response (200 OK):

[
    {
        "id": 1,
        "name": "General Chat",
        "created_at": "2025-11-23T10:00:00Z"
    },
    {
        "id": 2,
        "name": "Project Discussion",
        "created_at": "2025-11-23T11:30:00Z"
    }
]

cURL Example:

curl http://localhost:8000/api/chat/rooms/

2. Get Single Chat Room

GET /api/chat/rooms/{id}/

Get details of a specific room.

Request:

GET /api/chat/rooms/1/
Content-Type: application/json

Response (200 OK):

{
    "id": 1,
    "name": "General Chat",
    "created_at": "2025-11-23T10:00:00Z"
}

Response (404 Not Found):

{
    "detail": "Not found."
}

cURL Example:

curl http://localhost:8000/api/chat/rooms/1/

3. Create New Chat Room

POST /api/chat/rooms/

Create a new chat room.

Request:

POST /api/chat/rooms/
Content-Type: application/json

{
    "name": "Marketing Team"
}

Response (201 Created):

{
    "id": 3,
    "name": "Marketing Team",
    "created_at": "2025-11-23T12:00:00Z"
}

Response (400 Bad Request):

{
    "name": [
        "This field is required."
    ]
}

cURL Example:

curl -X POST http://localhost:8000/api/chat/rooms/ \
  -H "Content-Type: application/json" \
  -d '{"name": "Marketing Team"}'

4. Update Chat Room

PUT /api/chat/rooms/{id}/
PATCH /api/chat/rooms/{id}/

Update an existing chat room.

Request (PATCH - Partial Update):

PATCH /api/chat/rooms/1/
Content-Type: application/json

{
    "name": "Updated Room Name"
}

Response (200 OK):

{
    "id": 1,
    "name": "Updated Room Name",
    "created_at": "2025-11-23T10:00:00Z"
}

cURL Example:

curl -X PATCH http://localhost:8000/api/chat/rooms/1/ \
  -H "Content-Type: application/json" \
  -d '{"name": "Updated Room Name"}'

5. Delete Chat Room

DELETE /api/chat/rooms/{id}/

Delete a chat room and all its messages.

Request:

DELETE /api/chat/rooms/1/

Response (204 No Content):

(Empty response body)

cURL Example:

curl -X DELETE http://localhost:8000/api/chat/rooms/1/

6. Get Messages in a Room

GET /api/chat/rooms/{id}/messages/

Get all messages in a specific chat room.

Request:

GET /api/chat/rooms/1/messages/
Content-Type: application/json

Response (200 OK):

[
    {
        "id": 1,
        "sender": "john_doe",
        "content": "Hello everyone!",
        "created_at": "2025-11-23T10:05:00Z"
    },
    {
        "id": 2,
        "sender": "jane_smith",
        "content": "Hi John! Welcome to the chat.",
        "created_at": "2025-11-23T10:06:30Z"
    },
    {
        "id": 3,
        "sender": "john_doe",
        "content": "Thanks! Happy to be here.",
        "created_at": "2025-11-23T10:07:15Z"
    }
]

Response (Empty Room - 200 OK):

[]

cURL Example:

curl http://localhost:8000/api/chat/rooms/1/messages/