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.