Skip to content

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

Response Format#

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()
        })

HTTP Headers#

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