Real-time features feel like magic to users but the implementation is surprisingly straightforward once you understand the architecture. Here's exactly how I built the notification system powering SyntaxAndStories.

The Problem

Most social platforms need to tell you things instantly — someone liked your post, someone followed you, you got a new comment. Polling the server every few seconds is wasteful and slow. WebSockets solve this by keeping a persistent connection open between the browser and server.

The Stack

  • Django Channels — extends Django to handle WebSockets
  • Redis — acts as the channel layer, routing messages between consumers
  • Vanilla JavaScript — on the frontend

The Architecture

When a user logs in, the browser opens a WebSocket connection to a NotificationConsumer. This consumer joins a group named after the user — notifications_jay for example. When any part of the application needs to notify Jay, it sends a message to that group. Redis routes it to the right consumer, which pushes it to the browser instantly.


class NotificationConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope['user']
        self.group_name = f'notifications_{self.user.id}'
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )
        await self.accept()


    async def notify(self, event):
        await self.send(text_data=json.dumps(event['data']))


Sending Notifications

From anywhere in the codebase — a view, a signal, a Celery task:

channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
    f'notifications_{user.id}',
    {
        'type': 'notify',
        'data': {
            'message': 'Someone liked your post',
            'type': 'LIKE'
        }
    }
)

The Frontend

const socket = new WebSocket(`wss://syntaxandstories.dev/ws/notifications/`);
socket.onmessage = (e) => {
    const data = JSON.parse(e.data);
    showToast(data.message);
    updateUnreadCount();
};

What I Learned

The hardest part wasn't the WebSocket code — it was handling disconnections gracefully and making sure the channel layer stayed in sync across multiple server instances. Redis solves the multi-instance problem elegantly.

If you're building any kind of social or collaborative feature, Django Channels is worth the learning curve.