Library / SDK
em1208/adrf avatar
em1208/adrf

adrf gives Django REST framework views that are genuinely async

Async support for Django REST framework

620 stars50 forksPythonNOASSERTION

At a glance

What is it?
adrf is a small package that adds async class based views, function based views, viewsets, routers, serializers and generics to Django REST framework. The rule is strict, every handler method has to be async, and the async router has to replace the framework one.
Who is it for?
adrf fits a Django REST framework project that has already moved its data access to async and now needs its API layer to stop blocking the event loop, especially when the team wants await in the handler rather than a thread pool call per database hit. It is the wrong tool if only part of your stack is async, since mixing a synchronous handler with an async one raises at request time, and the version numbering at 0.1.x means the public API is still moving.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 73 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Django REST framework handlers are synchronous, adrf makes them awaitable

Django REST framework was built for synchronous views, and Django only gained native async view support in version 4.1. adrf is a package that fills that gap: its own description is async support for Django REST framework.

The requirement list in the readme is short. Python 3.8 or newer, Django 4.1 or newer, and the readme says it highly recommends and only officially supports the latest patch release of each Python and Django series.

The important part is the shape of the API. Instead of importing APIView from rest_framework.views, you import it from adrf.views, and instead of the rest_framework api_view decorator you use the one from adrf.decorators. The classes keep the familiar DRF shape, with authentication_classes, permission_classes and throttle_classes, but the handlers are coroutines.

That is what makes it a package rather than a patch. Django's async support is about the view function being a coroutine, not about the whole request path being async, so the parts of DRF that call your handler synchronously, the authentication class, the permission class, the throttle, the serializer, still had to be adapted. adrf supplies async variants of all of them, and the examples show exactly which ones to write yourself.

Every handler has to be async, and Django raises if it is not

adrf states the rule without hedging. For class based views, all handler methods must be async, otherwise Django will raise an exception. For function based views, the function itself must be async. For viewsets, all handler methods must be async too.

That strictness is the design decision the package is built around. A half-async view is worse than a sync one, because the failure surfaces at request time rather than at import time, and the exception Django raises will point at a framework internal rather than at the one method you forgot.

The example view shows what a complete async handler looks like, with async authentication, permission and throttle classes alongside an async get method, and the function based alternative using the adrf api_view decorator.

python
class AsyncView(APIView):
    authentication_classes = [AsyncAuthentication]
    permission_classes = [AsyncPermission]
    throttle_classes = [AsyncThrottle]

    async def get(self, request):
        return Response({"message": "This is an async class based view."})

from adrf.decorators import api_view

@api_view(['GET'])
async def async_view(request):
    return Response({"message": "This is an async function based view."})

The permission class in the readme shows the pattern for the supporting classes too: an async has_permission, an async has_object_permission and, for throttling, an async allow_request with a plain wait method that returns the delay.

The async router is the piece people miss

adrf ships a ModelViewSet implementation with asynchronous CRUD actions named aretrieve, acreate, aupdate and alist. The catch, stated directly in the readme, is that these actions are not invoked by default when you use the router implementation in rest_framework.

So a project that writes an async viewset, registers it with the standard router and expects async handlers to run gets the standard router behaviour instead. The fix is to use adrf's own router in urls.py, and the readme marks the import line with a comment to make the difference obvious.

python
from django.urls import path, include
from adrf import routers    # import the adrf router instead

from . import views

router = routers.DefaultRouter()
router.register(r"async_viewset", views.AsyncModelViewSet, basename="async")

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

This is the kind of detail that turns a five minute installation into an afternoon of debugging, and it is the one the readme puts a warning marker on. The failure mode is silent in the sense that nothing errors: the request succeeds, but through the synchronous action rather than the async one, and the only symptom is that your coroutine never runs.

The same pattern applies at the viewset layer. adrf.viewsets.ViewSet is imported in place of the DRF class, and the example viewset implements an async list and an async retrieve, the latter awaiting a query on the user model with afirst.

Serializers expose adata, which has to be awaited

The serializer layer follows the same rule. adrf.serializers.Serializer is the async counterpart, and its usage in the readme looks almost identical to DRF until the last line.

python
from .serializers import AsyncSerializer
from adrf.views import APIView

class AsyncView(APIView):
    async def get(self, request):
        data = {
            "username": "test",
            "password": "test",
            "age": 10,
        }
        serializer = AsyncSerializer(data=data)
        serializer.is_valid()
        return await serializer.adata

Two details are worth noticing. Validation is called as a plain method, serializer.is_valid(), exactly as it is in DRF; the difference arrives on the next line, where the result is reached through serializer.adata, which is awaited, rather than serializer.data. And the result is reached through serializer.adata, which is awaited, rather than serializer.data.

The async ModelSerializer works the same way in the generics section, where an adrf.generics.ListCreateAPIView is used with a plain queryset and serializer class, so the generic view handles the async plumbing and the model and serializer are written the way you would write them in an ordinary DRF project.

The dependency list explains where the async plumbing comes from. Beyond Django and Django REST framework, the package pulls in async-property at a 0.2.2 or newer release, which is what provides the awaitable property access that adata relies on.

Installing means pip plus one entry in INSTALLED_APPS

The installation is a pip install and a settings entry.

code
    pip install adrf
python
INSTALLED_APPS = [
    ...
    'adrf',
]

That is the whole documented setup. The package is a normal Python distribution built with poetry-core, and the project metadata lists the dependencies as Python 3.8 or newer, Django 4.1 or newer, djangorestframework 3.14.0 or newer and async-property 0.2.2 or newer.

The development setup is visible in the repository as well. The project uses poetry with a poetry.lock file, and the dev dependencies are pytest with pytest-cov and pytest-django, tox, faker for test data and ruff for linting, with the ruff configuration extending the rule set with import sorting. The coverage configuration measures the adrf package with branch coverage on and parallel mode, and the test runner is configured with short tracebacks, strict markers and the -ra summary flag.

So if you are contributing rather than consuming, the toolchain is poetry for packaging, pytest and tox for the test matrix, and ruff for style. For a user, the two steps above are all that is needed.

Where the all-or-nothing async rule bites

The obvious failure mode is the one the readme warns about: a view with one synchronous handler among async ones, which raises at request time. The second is subtler, and it is the silent one.

Nothing in adrf converts your synchronous code for you. The authentication class, the permission class and the throttle in the examples are all written as async classes with async methods, and the readme is careful to include a plain wait method on the throttle while allow_request is async. If you point the async view at your existing synchronous authentication class, the class attribute is accepted at class definition time and the problem appears when the request arrives.

The second limitation is about the framework underneath it. adrf requires Django 4.1 or newer, which is the release where Django added async view support in the first place. Below that floor there is no async request path to hook into, and the package does not offer a backport.

The third is the version number itself. The releases are 0.1.14 in August 2026, 0.1.13 in June 2026 and 0.1.12 in November 2025, so this is a package below 1.0 where the public API is still allowed to move. The readme does not document a deprecation policy, so the import paths in your code are something to check on each upgrade rather than something you can rely on being stable.

MIT in the metadata, a custom licence in the repository

There is a discrepancy worth resolving before you depend on this package. The project metadata in pyproject.toml declares the licence as MIT, while the repository's licence field is listed as a custom licence that GitHub cannot classify, with a note to see the LICENSE file.

That is a question about the text in the repository, not about the metadata, and the readme does not summarise the terms. If your organisation checks licences automatically, a package whose declared and shipped licences disagree will show up in that review, and the file in the repository is what governs.

On maintenance, the repository is not archived and the last push was on 2026-08-11, with the 0.1.14 release published the same day. That is a project being worked on, though at a 0.1.x version level the changes between releases are worth reading rather than skimming.

The alternative approach is worth naming, because it solves the same problem differently. Instead of making the DRF view layer async, you can keep the views synchronous and wrap the asynchronous parts explicitly, so that each call into async code is scheduled and awaited at the point of use while the rest of the request handling stays synchronous. adrf takes the opposite position: the handler and everything it touches, authentication, permissions, throttling, serialization, is async, which keeps one model end to end but removes the option of mixing. The choice is whether you want a uniform async path or a gradual one.

Editorial conclusion

adrf fits a Django REST framework project that has already moved its data access to async and now needs its API layer to stop blocking the event loop, especially when the team wants await in the handler rather than a thread pool call per database hit. It is the wrong tool if only part of your stack is async, since mixing a synchronous handler with an async one raises at request time, and the version numbering at 0.1.x means the public API is still moving. Check first that every handler in the view you convert is async, that you swap rest_framework's router for the one in adrf, and that the licence in the LICENSE file matches what your project metadata claims.

Frequently asked questions

How do I install adrf for Django REST framework?

Run pip install adrf and add 'adrf' to the INSTALLED_APPS setting. The package requires Python 3.8 or newer, Django 4.1 or newer, djangorestframework 3.14.0 or newer and async-property 0.2.2 or newer.

Do all handler methods in an adrf view have to be async?

Yes. The readme states that for class based views all handler methods must be async, otherwise Django raises an exception, and that the same applies to function based views and to viewsets.

Why are my async viewset actions not being called?

The async actions such as aretrieve, acreate, aupdate and alist are not invoked by default with the router in rest_framework. Import the router from adrf instead of rest_framework and register the viewset with it.

How do I use an async serializer in adrf?

Import Serializer from adrf.serializers, call serializer.is_valid() as a statement, and return await serializer.adata from the handler. The property triplet style of DRF does not apply here because the result is awaited.

Official sources

  1. em1208/adrf on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/em1208-adrf.svg)](https://hysenlabs.com/projects/em1208-adrf)

Was this useful?