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 Add rest_framework to your INSTALLED_APPS setting: INSTALLED_APPS = [ ... 'rest_framework', ] 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) This API returns: { "url": "http://127.0.0.1:8000/api/users/1/", "username": "admin", "email": "", "is_staff": true } 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 } } 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) 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) Then, configure Django REST Framework to use this renderer globally in settings.py: REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': [ 'api_template_response.renderers.CoreRenderer' ] } Testing with JWT Authentication Now, let's add JWT authentication by installing djangorestframework-simplejwt: pip install djangorestframework-simplejwt 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'), ] Example API Responses Failed Token Request curl --location --request POST 'http://127.0.0.1:8000/api/token/' Response: { "status": false, "version": "1.0.0", "error": { "username": ["This field is required."], "password": ["This field is required."] } } 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" }' Response: { "status": true, "version": "1.0.0", "data": { "refresh": "", "access": "" } } 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 If authentication fails, the response will be: { "status": false, "version": "1.0.0", "error": { "detail": "Authentication credentials were not provided." } } 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 } }

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
Add rest_framework
to your INSTALLED_APPS
setting:
INSTALLED_APPS = [
...
'rest_framework',
]
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)
This API returns:
{
"url": "http://127.0.0.1:8000/api/users/1/",
"username": "admin",
"email": "",
"is_staff": true
}
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
}
}
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)
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)
Then, configure Django REST Framework to use this renderer globally in settings.py
:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'api_template_response.renderers.CoreRenderer'
]
}
Testing with JWT Authentication
Now, let's add JWT authentication by installing djangorestframework-simplejwt
:
pip install djangorestframework-simplejwt
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'),
]
Example API Responses
Failed Token Request
curl --location --request POST 'http://127.0.0.1:8000/api/token/'
Response:
{
"status": false,
"version": "1.0.0",
"error": {
"username": ["This field is required."],
"password": ["This field is required."]
}
}
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"
}'
Response:
{
"status": true,
"version": "1.0.0",
"data": {
"refresh": "" ,
"access": ""
}
}
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
If authentication fails, the response will be:
{
"status": false,
"version": "1.0.0",
"error": {
"detail": "Authentication credentials were not provided."
}
}
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
}
}
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.