RESTful API
HTTP Methods
| Method |
Purpose |
ViewSet Method |
| GET |
Retrieve resource(s) |
list(), retrieve() |
| POST |
Create resource |
create() |
| PUT |
Update (full) |
update() |
| PATCH |
Update (partial) |
partial_update() |
| DELETE |
Delete resource |
destroy() |
Status Codes
from rest_framework import status
from rest_framework.response import Response
# Success
return Response(data, status=status.HTTP_200_OK) # OK
return Response(data, status=status.HTTP_201_CREATED) # Created
return Response(status=status.HTTP_204_NO_CONTENT) # No Content
# Client Error
return Response(errors, status=status.HTTP_400_BAD_REQUEST) # Bad Request
return Response(status=status.HTTP_401_UNAUTHORIZED) # Unauthorized
return Response(status=status.HTTP_403_FORBIDDEN) # Forbidden
return Response(status=status.HTTP_404_NOT_FOUND) # Not Found
return Response(status=status.HTTP_409_CONFLICT) # Conflict
# Server Error
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
RESTful Patterns
Resource Naming
✅ Good:
GET /api/posts/
POST /api/posts/
GET /api/posts/1/
PUT /api/posts/1/
DELETE /api/posts/1/
❌ Bad:
GET /api/getPosts
POST /api/createPost
GET /api/post/1/get
Nested Resources
✅ Good:
GET /api/posts/1/comments/
POST /api/posts/1/comments/
GET /api/posts/1/comments/2/
Query Parameters
GET /api/posts/?search=django&ordering=-created_at&page=2
Success Response
# Single object
{
"id": 1,
"title": "My Post",
"content": "Content here"
}
# List
[
{"id": 1, "title": "Post 1"},
{"id": 2, "title": "Post 2"}
]
# With pagination
{
"count": 100,
"next": "http://api.example.com/posts/?page=2",
"previous": null,
"results": [...]
}
Error Response
# Validation errors
{
"title": ["This field is required."],
"content": ["This field may not be blank."]
}
# General error
{
"detail": "Not found."
}
Custom Response
from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet):
def list(self, request):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
return Response({
'success': True,
'data': serializer.data,
'count': queryset.count()
})
from rest_framework.response import Response
def custom_view(request):
response = Response({'data': 'value'})
response['X-Custom-Header'] = 'value'
return response
Next: Query Parameters & Filtering