# djangochannelsrestframework gives Channels 4 websockets a REST-shaped API

> Django Channels Rest Framework lets you write websocket consumers that look like Django REST Framework views, with model mixins, permissions, instance subscriptions and custom actions over a JSON message protocol. It ships as a small MIT licensed package on top of Django, Channels and DRF, and it is built on a fork of the older channels-api project.

**NilCoalescing/djangochannelsrestframework** — A Rest-framework for websockets using Django channels-v4

- Repository: https://github.com/NilCoalescing/djangochannelsrestframework
- Website: https://djangochannelsrestframework.readthedocs.io/en/latest/
- Stars: 651 · Forks: 83
- Language: Python
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/nilcoalescing-djangochannelsrestframework

## The same view and serializer objects, over a websocket

Django Channels Rest Framework provides a DRF-like interface for building channels-v4 websocket consumers. The goal, as the project puts it, is to provide an API that is familiar to DRF users.

That familiarity is the whole product. A Django developer who has written REST views already knows how a queryset, a serializer class and a permission class fit together, and this package lets you reuse those same objects in a websocket consumer. The README is explicit that you may use the same querysets and serializer_class utilised in your DRF views, with one exception, you must omit the DRF permissions.

The reason for that exception is that the framework ships its own permission classes, imported from djangochannelsrestframework, which provide the standard AllowAny and IsAuthenticated. Permissions in DRF are tied to the request and view context, and the websocket consumer has a different context, so the framework provides its own rather than trying to make the DRF ones work.

The project can be used alongside HyperMediaChannels and ChannelsMultiplexer to create a hypermedia style API over websockets, and it is also designed to work as a free standing framework, which is the mode most teams will start with.

For history, the package is based on a fork of the channels-api project by linuxlewis, and is inspired by Django Rest Framework. A JavaScript client library called dcrf-client, developed by theY4Kman, exists for talking to it from a browser.

## Installation and the as_asgi() rule

Installing the package is one command:

```bash
  pip install djangochannelsrestframework
```

The declared requirements are in the package metadata, and they are specific. Django 4.2.16 or newer, Channels 4.1.0 or newer and Django REST Framework 3.15.2 or newer, on Python 3.8 or newer. The classifiers describe the project as a production stable web environment package for developers, and the licence is MIT.

The documentation puts a warning immediately after the install command, and it is the one thing to get right on day one. When you declare your consumers in your application definition it is very important to use the as_asgi() class method, for example MyConsumer.as_asgi(), and you should not have any instances of MyConsumer() in your code base.

That is a small detail with a large failure mode. A consumer is a class, and Channels expects an ASGI application, which the as_asgi() method provides. Writing MyConsumer() in a URL route creates an instance instead, and the symptom appears when the websocket connects rather than when the code is written.

The development dependencies show how the project is tested. The test extras pull in channels with the daphne server, pytest, pytest-django and pytest-asyncio, plus coverage, and the requirements file for the docs side adds sphinx with the read the docs theme and a pinned black for formatting.

## Mixins decide which actions a consumer exposes

A consumer in this framework is built from a generic base class and whichever mixins you need, and the mixin list is short enough to read as a table.

There is ListModelMixin exposing list, PatchModelMixin exposing patch, CreateModelMixin exposing create, RetrieveModelMixin exposing retrieve, UpdateModelMixin exposing update and DeleteModelMixin exposing delete. You add only the ones your consumer should support, and the client invokes them by name.

The base class is GenericAsyncAPIConsumer, which works much like GenericAPIView in DRF. Here is a complete consumer:

```python
    class LiveConsumer(ListModelMixin, GenericAsyncAPIConsumer):
        queryset = models.Test.objects.all()
        serializer_class = serializers.TestSerializer
        permission_classes = (permissions.IsAuthenticated,)
```

Because that class uses the ListModelMixin, one has access to the list action.

The client invokes an action by sending a websocket message naming it, with an action field and a request_id, and the documented example is the message {action: "list", "request_id": 42}.

The request_id is the important part of the protocol. Every response carries it back, which is what lets a client have several requests in flight over one socket and match each reply to the call that caused it. For a consumer that also pushes updates, the same identifier is reused for every update tied to that subscription.

## The message format is the contract with the client

Because this is a framework rather than a library, the shape of the messages is the thing clients depend on, and the README documents both directions.

A request is a JSON object with an action and a request_id. A response carries an action, an errors list, a response_status, the request_id and a data payload. Here is the shape of a server-side update:

```python
  {
    "action": "update",
    "errors": [],
    "response_status": 200,
    "request_id": 4,
    "data": {'email': '42@example.com', 'id': 42, 'username': 'thenewname'},
}
```

Two things in that payload are worth noticing. The status is a number carried in the message body rather than an HTTP status on the connection, because there is no HTTP response in a websocket exchange. And the errors list is always present, so a client can branch on a single shape whether the call succeeded or failed.

The data field holds the serialized object, which is where the reuse of DRF serializers pays off. A client receives the same representation it would get from a REST endpoint, with the same field names and the same validation, without the framework having to invent a second serialization format for websockets.

## Subscribing to a single model instance

Beyond request and response, the framework can push changes to a client that has subscribed to one model instance.

A consumer that mixes in ObserverModelInstanceMixin exposes the retrieve, subscribe_instance and unsubscribe_instance actions:

```python
   class TestConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer):
       queryset = get_user_model().objects.all()
       serializer_class = UserSerializer
```

Subscribing is a message with the action, the primary key of the instance and the request id that will be used for all result updates:

```python
   {
       "action": "subscribe_instance",
       "pk": 42,  # the id of the instance you are subscribing to
       "request_id": 4  # this id will be used for all result updates.
   }
```

So the client is not polling. It subscribes once, and every later change to that instance arrives as an update message on the same socket, tagged with the same request_id. For a screen that shows one record, that removes a polling loop and the load it puts on the server.

The same mechanism works for custom Django signals, using the observer decorator. The documentation shows a signal created with Signal() and a consumer that subscribes to it and can trigger it manually, which covers the case where the change you want to broadcast is not a model save but something your own code raises.

## Custom actions, sync methods and the database rule

Actions that are not part of the CRUD set are added with the action decorator, and the decorator handles the async detail.

An async action is awaited, and a sync action is already wrapped in database_sync_to_async, so a plain method can call the ORM directly:

```python
           user = await database_sync_to_async(self.get_object)(pk=pk)
           # ... do some stuff
           # remember to wrap all db actions in `database_sync_to_async`
           return {}, 200  # return the context and the response code.
```

The return value is a pair, the context and the response code, and the code becomes the response_status in the message. A custom action may also take named arguments, which arrive from the client message.

The rule about the database is the one to internalise. In an async action, every database call has to be wrapped in database_sync_to_async, and the documentation repeats it as a comment in the example. That is a direct consequence of running an async consumer, and it is the most common source of surprises when moving a DRF view into this framework, because a DRF view is sync and never had to think about it.

Consumers that are not bound to any model at all are supported too, using AsyncAPIConsumer with the same action decorator, which covers the case of a websocket that only exposes custom commands.

## Running ordinary Django views over the socket

The framework is not limited to model-backed consumers. The view_as_consumer helper lets an existing Django view be served over a websocket connection:

```python
  from djangochannelsrestframework.consumers import view_as_consumer

  application = ProtocolTypeRouter({
      "websocket": AuthMiddlewareStack(
          URLRouter([
              url(r"^front(end)/$", view_as_consumer(YourDjangoView)),
          ])
      ),
   })
```

The wiring is ordinary Channels, a protocol type router, the auth middleware stack and a URL router, with the view wrapped by the helper instead of being a consumer subclass.

The parameters a view expects are supplied in the message. If the view reads GET query string values you provide them with the query option, and if the view method reads parameters from the URL you provide them with the parameters option. A message then looks like this:

```javascript
    {
      action: "retrieve",
      query: {"user_id": 42}
      parameters: {"project_id": 92}
    }
```

Sending that results in a GET request being evaluated on the view. That is a useful escape hatch, because it means a view that already exists and is already tested can be exposed over a websocket without rewriting it as a consumer.

On maintenance, the project is not archived, the last push was on 2026-08-04, and the release tags are v1.3.0 from 2025-02-12, v1.2.0 from 2023-08-20 and v1.1.0 from 2022-07-10. The MIT licence and the declared production stable status make it a safe dependency, and the documentation is published on Read the Docs with the source, the tests and a security policy in the repository.

## Conclusion

Django Channels Rest Framework suits a Django team that already uses Django REST Framework and wants the same view and serializer objects reused over a websocket, and it is the wrong choice for a project that wants raw websocket control or a hypermedia API, since the project itself points at HyperMediaChannels and ChannelsMultiplexer for that. Before adopting it, confirm your Django is at least 4.2.16 and Channels at least 4.1.0, and follow the as_asgi() instruction, because a consumer instantiated directly rather than bound as an ASGI application is the mistake the documentation warns about first.

## FAQ

### What does Django Channels Rest Framework do?

It provides a DRF-like interface for building channels-v4 websocket consumers, so you can reuse the same querysets, serializer classes and permission style you already use in Django REST Framework views, and expose CRUD actions over a websocket connection.

### How do I install djangochannelsrestframework?

Run pip install djangochannelsrestframework. The package requires Python 3.8 or newer, Django 4.2.16 or newer, Channels 4.1.0 or newer and Django REST Framework 3.15.2 or newer.

### How do I call an action from a websocket client?

Send a JSON message naming the action and a request_id, for example a message with an action of list and a request_id of 42. Every response and every pushed update carries the request_id back, so a client can match replies to calls.

### How do I push model changes to a client?

Mix ObserverModelInstanceMixin into your consumer, which exposes the retrieve, subscribe_instance and unsubscribe_instance actions. The client subscribes with the action, the pk of the instance and a request_id, and later changes arrive as update messages on the same connection.

### Can I expose an existing Django view over a websocket?

Yes. The view_as_consumer helper wraps a view so it can be routed with a URLRouter, and the client supplies the view's parameters through the query and parameters options in the message, with a retrieve action producing a GET request on the view.

## Sources

- [License: MIT](https://github.com/NilCoalescing/djangochannelsrestframework/blob/master/LICENSE)
- [NilCoalescing/djangochannelsrestframework on GitHub](https://github.com/NilCoalescing/djangochannelsrestframework)
- [Project website](https://djangochannelsrestframework.readthedocs.io/en/latest/)
- [README](https://github.com/NilCoalescing/djangochannelsrestframework/blob/master/README.md)
- [Releases](https://github.com/NilCoalescing/djangochannelsrestframework/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nilcoalescing-djangochannelsrestframework
