Skip to content

DRF Fundamentals

DRF (Django REST Framework) is the standard toolkit for building APIs in Django projects. It gives you a clean structure for handling request parsing, validation, business logic, authentication, permissions, and response formatting. In real projects, DRF helps you move from “just returning JSON” to a consistent API architecture that frontend apps, mobile clients, and third-party integrations can trust.

After this chapter, you should be able to explain how an API request flows through a DRF app, identify when to use each HTTP method, interpret status codes correctly, and understand core DRF concepts such as serializers, views, and authentication.

API Thinking

Learn how clients and servers communicate through request/response contracts.

REST Principles

Understand resources, endpoints, and stateless architecture.

DRF Building Blocks

Connect serializers, views, and routers into a maintainable API.

  1. Install DRF in your Django project:

    Terminal window
    uv add djangorestframework
  2. Register DRF in Django settings:

    # settings.py
    INSTALLED_APPS = [
    # other apps
    "rest_framework",
    ]
  3. Add baseline REST framework configuration:

    # settings.py
    REST_FRAMEWORK = {
    # Keep decimals as numbers in JSON responses instead of converting to strings (optional, but often desirable for APIs that return numeric data)
    'COERCE_DECIMAL_TO_STRING': False,
    }

An API (Application Programming Interface) is a formal communication layer between software systems. Instead of exposing internal database structure or application code directly, an API exposes carefully designed endpoints. Each endpoint accepts specific inputs and returns structured outputs, allowing different systems to work together without tightly coupling their implementations.

In practical terms, an API is a contract. The client agrees to send requests in a known format, and the server agrees to process those requests and return predictable responses. This contract allows teams to work independently: backend engineers can evolve internals while frontend engineers continue integrating through stable API definitions.

The following diagram represents the exact flow you shared, translated to Mermaid syntax:

graph LR Client --> API API --> DB DB --> API API --> Client Client[Client App] API[API Server] DB[Database]

This flow is the heart of backend API design. The client asks for a resource, the API server validates and processes that request, the database provides persisted data, and the API formats a response. DRF sits in the API server layer and gives you reusable tools for input validation, serialization, authentication, and permission checks.

REST (Representational State Transfer) is an architectural style, not a strict protocol. A RESTful API organizes data into resources and exposes them through URLs. Clients interact with those resources using HTTP methods such as GET, POST, PUT, PATCH, and DELETE.

A key REST idea is statelessness. Every request should carry all required context (authentication token, parameters, payload), so the server does not depend on hidden conversation state between calls. Stateless design improves scalability and makes APIs easier to cache, test, and debug.

graph LR R["Resource: users"] --> E1["GET /users/"] R --> E2["POST /users/"] R --> E3["GET /users/:id/"] R --> E4["PATCH /users/:id/"] R --> E5["DELETE /users/:id/"]

HTTP methods communicate intent. Choosing the right method makes your API predictable and easy to consume.

GET retrieves data and should not change server state. It is considered a safe method and is commonly cached by browsers and proxies.

Status codes describe result semantics. They are not optional decoration; they are part of the API contract and guide client-side behavior.

graph TD A[HTTP Response] --> B[1xx Informational] A --> C[2xx Success] A --> D[3xx Redirection] A --> E[4xx Client Error] A --> F[5xx Server Error]
Status CodeMeaningTypical DRF Scenario
200 OKRequest succeeded and response body is presentList or retrieve endpoint
201 CreatedNew resource successfully createdSuccessful POST create
204 No ContentRequest succeeded with empty response bodySuccessful DELETE
400 Bad RequestInput failed validationSerializer errors
401 UnauthorizedUser is not authenticatedMissing/invalid auth token
403 ForbiddenUser is authenticated but not allowedPermission denied
404 Not FoundResource does not existInvalid object ID
500 Internal Server ErrorUnexpected server failureUnhandled backend exception

JSON (JavaScript Object Notation) is the default data format for most REST APIs because it is language-agnostic, lightweight, and easy to parse in browsers, mobile apps, and backend services. In DRF, serializers convert Django model instances into JSON-compatible Python primitives, then renderers encode that data into JSON responses.

A good JSON payload is stable, explicit, and documented. Field naming, nullability, nested structure, and data types should remain consistent over time so clients do not break unexpectedly.

{
"id": 1,
"name": "Kumar Sahil",
"email": "krsahil8825@gmail.com",
"is_active": true,
"roles": ["admin", "editor"]
}

Serializer

Translates model/queryset data into JSON and validates incoming request data before saving.

APIView / ViewSet

Receives HTTP requests, applies authentication/permissions, calls serializer logic, and returns responses.

Router

Automatically generates URL patterns for ViewSets to keep API routing consistent.

Authentication

Verifies identity (who the user is), e.g., session, token, or JWT strategy.

Permission

Authorizes actions (what the user can do), e.g., read-only for anonymous users.

Pagination

Splits large result sets into pages for performance and better client UX.

graph TD Req[Incoming Request] --> Auth[Authentication] Auth --> Perm[Permission Check] Perm --> View[APIView or ViewSet] View --> Ser[Serializer Validate or Serialize] Ser --> DB[(Database)] DB --> Ser Ser --> Res[JSON Response]
EndpointMethodPurpose
/users/GETReturn paginated user list
/users/POSTCreate a new user
/users/{id}/GETRetrieve one user
/users/{id}/PATCHPartially update user
/users/{id}/DELETERemove user
  • Django: Focuses on server-rendered HTML views, forms, and template rendering. It is ideal for traditional web applications where the server generates the entire page. Django’s views and forms are designed around this request-response cycle.

  • DRF: Focuses on building APIs that return structured data (usually JSON) for consumption by clients like SPAs, mobile apps, or third-party services. DRF provides serializers for data validation and transformation, and views that handle API-specific logic like authentication and permissions.

  • Use Django views and templates when building a monolithic web application where the server renders HTML pages directly to users.

  • Use DRF when you need to expose an API for frontend frameworks (React, Vue), mobile apps, or third-party integrations. DRF allows you to build a clean separation between your backend logic and how clients consume data.

Django and DRF both have own request and response objects. DRF’s Request extends Django’s HttpRequest to provide additional functionality for parsing request data, handling authentication, and managing content negotiation. Similarly, DRF’s Response extends Django’s HttpResponse to simplify returning JSON responses with appropriate status codes and content types.

To use DRF’s Request and Response, you typically import them from rest_framework and use them in your API views. For example:

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
@api_view()
def my_api_view(request):
# DRF's Request object is used here
data = {"message": "Hello, DRF!"}
# DRF's Response object is used to return JSON data
return Response(data)

The @api_view decorator is a function provided by DRF that allows you to create function-based API views. It takes a list of HTTP methods (e.g., ['GET', 'POST']) that the view should respond to. When you use @api_view, DRF automatically wraps your function in a way that it can handle API requests and responses, including content negotiation, authentication, and permission checks.

Default api view accepts only GET method, so if you want to allow other methods like POST, PATCH, etc., you need to specify them in the decorator. For example:

from rest_framework.decorators import api_view
@api_view(['GET', 'POST'])
def my_api_view(request):
if request.method == 'GET':
# Handle GET request
pass
elif request.method == 'POST':
# Handle POST request
pass

The status module in DRF provides a set of named constants for HTTP status codes. Instead of remembering numeric codes like 200, 404, or 500, you can use descriptive names like status.HTTP_200_OK, status.HTTP_404_NOT_FOUND, and status.HTTP_500_INTERNAL_SERVER_ERROR. This improves code readability and makes it clear what each response status represents.

When returning a response in DRF, you can use the status module to set the appropriate status code. For example:

from rest_framework import status
from rest_framework.response import Response
def my_api_view(request):
data = {"message": "Hello, DRF!"}
return Response(data, status=status.HTTP_200_OK)