DEV Community

Cover image for Standardizing API Responses in Django REST Framework: A Step-by-Step Guide
Ngoc Bui
Ngoc Bui

Posted on

Standardizing API Responses in Django REST Framework: A Step-by-Step Guide

1. Introduction

When building APIs in Django, having a structured response format improves consistency and maintainability. In this post, we’ll set up a simple API response template to standardize success and error responses in Django REST framework.

2. The Problem

First, we create a new Django project and install Django REST framework. See the demo code:

python3 -m django startproject api_template_response
cd api_template_response
python3 -m venv env
source env/bin/activate
pip install djangorestframework
Enter fullscreen mode Exit fullscreen mode

Add rest_framework to your INSTALLED_APPS setting:

INSTALLED_APPS = [
    ...
    'rest_framework',
]
Enter fullscreen mode Exit fullscreen mode

Example User API Response

Consider the following Django API view for fetching user data:

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['url', 'username', 'email', 'is_staff']

class UserAPIView(views.APIView):
    def get(self, request):
        users = User.objects.all()
        serializer = UserSerializer(users, many=True, context={'request': request})
        return Response(serializer.data)
Enter fullscreen mode Exit fullscreen mode

This API returns:

{
    "url": "http://127.0.0.1:8000/api/users/1/",
    "username": "admin",
    "email": "",
    "is_staff": true
}
Enter fullscreen mode Exit fullscreen mode

However, many projects prefer using a standardized API response template, such as:

{
    "success": true,
    "version": "1.0.0",
    "data": {
        "url": "http://127.0.0.1:8000/api/users/1/",
        "username": "admin",
        "email": "",
        "is_staff": true
    }
}
Enter fullscreen mode Exit fullscreen mode

3. API Response Template

To achieve this, we create a helper function for consistent API responses:

def handle_200_response(data):
    response_data = {
        "version": "1.0.0",
        "status": True,
        "data": data,
    }
    return Response(response_data, status=200)
Enter fullscreen mode Exit fullscreen mode

Using this function ensures all success responses follow the same format. However, if you are using Django ViewSets, manually overriding every function (list, create, destroy, etc.) can become complex.

4. Applying the Template to All APIs

Django REST Framework provides custom renderers that allow us to modify API responses globally. Let's create a custom renderer:

# api_template_response/renderers.py
from rest_framework.renderers import JSONRenderer
from rest_framework import status

class CoreRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        status_code = renderer_context["response"].status_code
        is_success = status.is_success(status_code)

        response_data = {
            "status": is_success,
            "version": "1.0.0",
        }

        if is_success:
            response_data["data"] = data
        else:
            response_data["error"] = data

        return super().render(response_data, accepted_media_type, renderer_context)
Enter fullscreen mode Exit fullscreen mode

Then, configure Django REST Framework to use this renderer globally in settings.py:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'api_template_response.renderers.CoreRenderer'
    ]
}
Enter fullscreen mode Exit fullscreen mode

Testing with JWT Authentication

Now, let's add JWT authentication by installing djangorestframework-simplejwt:

pip install djangorestframework-simplejwt
Enter fullscreen mode Exit fullscreen mode

Then, configure the authentication endpoints in urls.py:

from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
Enter fullscreen mode Exit fullscreen mode

Example API Responses

Failed Token Request

curl --location --request POST 'http://127.0.0.1:8000/api/token/'
Enter fullscreen mode Exit fullscreen mode

Response:

{
    "status": false,
    "version": "1.0.0",
    "error": {
        "username": ["This field is required."],
        "password": ["This field is required."]
    }
}
Enter fullscreen mode Exit fullscreen mode

Successful Token Request

curl --location --request POST 'http://127.0.0.1:8000/api/token/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": "admin",
    "password": "admin"
}'
Enter fullscreen mode Exit fullscreen mode

Response:

{
    "status": true,
    "version": "1.0.0",
    "data": {
        "refresh": "<JWT_REFRESH_TOKEN>",
        "access": "<JWT_ACCESS_TOKEN>"
    }
}
Enter fullscreen mode Exit fullscreen mode

Adding Authentication to the ViewSet

from rest_framework.permissions import IsAuthenticated

class UserViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAuthenticated]
    queryset = User.objects.all()
    serializer_class = UserSerializer
Enter fullscreen mode Exit fullscreen mode

If authentication fails, the response will be:

{
    "status": false,
    "version": "1.0.0",
    "error": {
        "detail": "Authentication credentials were not provided."
    }
}
Enter fullscreen mode Exit fullscreen mode

If successful:

{
    "status": true,
    "version": "1.0.0",
    "data": {
        "url": "http://127.0.0.1:8000/api/users/1/",
        "username": "admin",
        "email": "",
        "is_staff": true
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Using a structured API response template enhances consistency, simplifies debugging, and improves API usability. Implement this approach in your Django project to maintain clean and predictable API responses.

See the demo code.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.