Customizing API Responses in Django Rest Framework (DRF)
When building REST APIs, it’s crucial to ensure that responses are well-structured, informative, and consistent. In this section, we will cover:
- Returning JSON Responses – How to send structured data back to the client.
- Customizing Response Format – Formatting the response to include metadata like status and total records.
- Handling Errors & Exceptions – Ensuring meaningful error messages are returned for better debugging.
Returning JSON Responses
Updating views.py to return a structured JSON response
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer
class BookListCustomResponseView(APIView):
def get(self, request):
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
# Customized response format
response_data = {
‘status’: ‘success’,
‘total_books’: len(serializer.data),
‘books’: serializer.data,
}
return Response(response_data, status=status.HTTP_200_OK)
Adding URL Path for the Custom Response View (urls.py)
from .views import BookListCustomResponseView
urlpatterns += [
path(‘books/custom-list/’, BookListCustomResponseView.as_view(), name=’custom-list’),
]
Explanation:
1. Returning JSON Responses
- In Django Rest Framework, the Response object is used to return JSON responses.
- The Response class formats the data properly and ensures the correct content type (application/json).
- Instead of returning plain serialized data, we structure the response with additional details.
2. Customizing Response Format
- The API response includes:
- status: Indicates whether the request was successful.
- total_books: Provides the count of books in the response.
- books: Contains the list of books serialized in JSON format.
- This structure makes it easier for frontend applications or consumers of the API to process the data.
3. Handling Errors & Exceptions
- If an error occurs (e.g., no books found, invalid request), DRF provides built-in exception handling.
In this case, we return a 200 OK response even if there are no books, but we could modify the logic to return a 404 Not Found or 400 Bad Request if needed.
Example API Response
If there are two books in the database, the API would return:
{
“status”: “success”,
“total_books”: 2,
“books”: [
{
“id”: 1,
“title”: “Django for Beginners”,
“author”: “William S. Vincent”,
“price”: 29.99,
“published_date”: “2023-05-10”
},
{
“id”: 2,
“title”: “Python API Development”,
“author”: “John Doe”,
“price”: 39.99,
“published_date”: “2024-01-15”
}
]
}
Why Customizing API Responses Matters?
✅ Improved Readability – Makes it easier for API consumers to understand the response.
✅ Standardization—Ensures all responses follow a consistent format.
✅Better Debugging—Helps in troubleshooting issues by providing extra details like status messages.
Handling Errors & Exceptions in Django Rest Framework (DRF)
While building REST APIs, it’s important to handle errors gracefully and return meaningful responses to API consumers. In this section, we’ll cover:
✅ Handling Errors in API Views
✅ Returning Custom Error Messages
✅ Using DRF’s Built-in Exception Handling
Enhancing views.py with Error Handling
Updated views.py:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.core.exceptions import ObjectDoesNotExist
from .models import Book
from .serializers import BookSerializer
class BookListCustomResponseView(APIView):
def get(self, request):
try:
books = Book.objects.all()
# Handle case when no books are found
if not books.exists():
return Response(
{“status”: “error”, “message”: “No books found”},
status=status.HTTP_404_NOT_FOUND
)
serializer = BookSerializer(books, many=True)
# Customized response format
response_data = {
“status”: “success”,
“total_books”: len(serializer.data),
“books”: serializer.data,
}
return Response(response_data, status=status.HTTP_200_OK)
except Exception as e:
return Response(
{“status”: “error”, “message”: str(e)},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
Adding Custom Error Handling in Django Rest Framework
1. Handling No Records Found (404 Not Found)
- We check if books.exists() before serialization.
If no records exist, we return:
{
“status”: “error”,
“message”: “No books found”
}
- This prevents returning an empty list without explanation.
2. Handling Unexpected Errors (500 Internal Server Error)
- Any unexpected error is caught using except Exception as e.
- The actual error message is returned in the response.
Example response in case of an unknown error:
{
“status”: “error”,
“message”: “Some error occurred”
}
Example API Responses (After Adding Error Handling)
Success Response (Books Found)
{
“status”: “success”,
“total_books”: 2,
“books”: [
{
“id”: 1,
“title”: “Django for Beginners”,
“author”: “William S. Vincent”,
“price”: 29.99,
“published_date”: “2023-05-10”
},
{
“id”: 2,
“title”: “Python API Development”,
“author”: “John Doe”,
“price”: 39.99,
“published_date”: “2024-01-15”
}
]
}
Error Response (No Books in Database)
{
“status”: “error”,
“message”: “No books found”
}
Error Response (Unexpected Server Error)
{
“status”: “error”,
“message”: “Some error occurred”
}
Why Custom Error Handling Matters?
✅ Improves API Reliability – Prevents unexpected crashes.
✅ Better Developer Experience – Clear error messages make debugging easier.
✅Enhances API Consumer Experience – Clients can handle errors properly based on response codes.
What Does This Code Do?
After implementing this code:
- Creates a New API Endpoint
- The new URL /api/books/custom-list/ is added.
- This allows users to send a Get request to create a book.
- The new URL /api/books/custom-list/ is added.
- Returning JSON Responses:
- The API fetches data from the database and serializes it into a JSON response.
- Instead of returning plain serialized data, the response is structured with metadata like status and count.
- Customizing Response Format:
- The BookListCustomResponseView formats the response as { “status”: “success”, “total_books”: <count>, “books”: <data> }.
- Handling Errors & Exceptions:
- If no books exist, a { “status”: “error”, “message”: “No books found” } response is returned with 404 status.
If an unexpected error occurs, it returns { “status”: “error”, “message”: “Some error occurred” } with 500 status.
How Does It Work? (Step-by-Step Execution)
1️⃣ Client Makes a GET Request
The client (frontend or API consumer) sends a GET request to /books/custom-list/.Like below:
2️⃣ Django Views Handle the Request
- The BookListCustomResponseView class receives the request.
3️⃣ Fetch Data from Database
- Book.objects.all() retrieves all book records from the database.
4️⃣ Serialize the Data
- The BookSerializer(books, many=True) converts the queryset into JSON format.
5️⃣ Customize the Response
A custom dictionary is created:
{
“status”: “success”,
“total_books”: <count>,
“books”: <data>
}
- This ensures the response is structured and informative.
6️⃣ Return the Response with HTTP 200 Status
- The formatted response is returned using Response(response_data, status=status.HTTP_200_OK).
7️⃣ Client Receives the Response
- The frontend/API consumer receives a JSON response with book details, status, and total count.
✅ If there are no books, an empty list is returned.
✅ If an error occurs, DRF’s built-in exception handling provides a relevant error message.
Exception Handling
8️⃣ Handling Missing Data or Empty Queryset
If no books exist, the response still returns a success message with an empty list:
{
“status”: “success”,
“total_books”: 0,
“books”: []
}
9️⃣ Handling 400 Errors (Bad Request)
- If the request is malformed (e.g., missing required parameters in a POST request), DRF automatically raises a 400 Bad Request response.
Exercise:
- Override the create() method in your StudentViewSet to return a custom success message.
- Example: {“message”: “Student created successfully!”}