API Thinking
Learn how clients and servers talk to each other using requests and responses.
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 proper, consistent API setup that frontend apps, mobile apps, and other services can rely on.
After this chapter, you should be able to explain how an API request moves through a DRF app, know when to use each HTTP method, understand status codes correctly, and understand the main DRF building blocks such as serializers, views, and authentication.
API Thinking
Learn how clients and servers talk to each other using requests and responses.
REST Principles
Understand resources, endpoints, and stateless design.
DRF Building Blocks
Connect serializers, views, and routers together into a maintainable API.
Install DRF in your Django project:
uv add djangorestframeworkRegister DRF in Django settings:
# settings.pyINSTALLED_APPS = [ # other apps "rest_framework",]Add baseline REST framework configuration:
# settings.pyREST_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 proper communication layer between two software systems. Instead of exposing your internal database structure or app code directly, an API exposes carefully designed endpoints instead. Each endpoint accepts certain inputs and returns structured outputs, which lets different systems work together without being tightly tied to each other’s internal code.
In simple words, an API is like a contract. The client agrees to send requests in a known format, and the server agrees to process those requests and send back predictable responses. This contract lets teams work independently: backend engineers can keep changing the internal code, while frontend engineers keep building on top of the same stable API.
The diagram below shows the same flow in a simple visual form:
This flow is at the heart of backend API design. The client asks for something, the API server checks and processes that request, the database gives back the stored data, and the API then sends back a properly formatted response. DRF sits inside the API server layer and gives you ready-made tools for input validation, serialization, authentication, and permission checks.
REST (Representational State Transfer) is a style of designing APIs, not a strict set of rules you must follow exactly. A RESTful API organizes data into “resources” and exposes them through URLs. Clients work with these resources using HTTP methods such as GET, POST, PUT, PATCH, and DELETE.
One key REST idea is being stateless. Every request should carry everything the server needs (authentication token, parameters, data), so the server does not need to remember anything from previous requests. This stateless design makes APIs easier to scale, cache, test, and debug.
HTTP methods tell the server what you actually want to do. Choosing the right method makes your API predictable and easy to understand.
GET is used to fetch data and should not change anything on the server. It is considered a safe method and is often cached by browsers and proxies.
POST is used to create new resources or trigger actions that are not repeatable in a safe way. Sending the same POST again usually creates extra records, unless you add some logic to prevent duplicates.
PUT usually replaces the whole resource with a new version, while PATCH updates only the specific fields you send. DRF supports both, and serializers help you validate full or partial data either way.
DELETE removes a resource (or marks it as deleted, in soft-delete setups). A successful delete usually returns 204 No Content.
Status codes tell you what actually happened with your request. They are not just extra detail, they are part of the API’s contract and help the client decide what to do next.
| Status Code | Meaning | Typical DRF Scenario |
|---|---|---|
| 200 OK | Request worked and the response has data in it | List or retrieve endpoint |
| 201 Created | A new resource was created successfully | Successful POST create |
| 204 No Content | Request worked but there is no body in the response | Successful DELETE |
| 400 Bad Request | The input failed validation | Serializer errors |
| 401 Unauthorized | The user is not logged in / not authenticated | Missing/invalid auth token |
| 403 Forbidden | The user is logged in but is not allowed to do this | Permission denied |
| 404 Not Found | The resource does not exist | Invalid object ID |
| 500 Internal Server Error | Something unexpected went wrong on the server | Unhandled backend exception |
JSON (JavaScript Object Notation) is the default data format used by most REST APIs, because it does not depend on any specific programming language, it is lightweight, and it is easy to read in browsers, mobile apps, and backend services. In DRF, serializers turn Django model instances into JSON-friendly Python values, and then renderers turn that data into an actual JSON response.
A good JSON response stays consistent and well documented. Field names, whether a value can be empty (null), nested structures, and data types should stay the same over time, so that clients do not suddenly break.
{ "id": 1, "name": "Kumar Sahil", "email": "krsahil8825@gmail.com", "is_active": true, "roles": ["admin", "editor"]}Serializer
Turns model/queryset data into JSON, and checks incoming request data before it gets saved.
APIView / ViewSet
Receives the HTTP request, applies authentication and permission checks, calls the serializer, and sends back the response.
Router
Automatically creates URL patterns for ViewSets, so your API routing stays consistent.
Authentication
Checks who the user actually is, for example through session, token, or JWT login.
Permission
Decides what the user is allowed to do, for example read-only access for users who are not logged in.
Pagination
Splits large lists of results into smaller pages, for better performance and a better experience for the client.
| Endpoint | Method | Purpose |
|---|---|---|
/users/ | GET | Return a paginated list of users |
/users/ | POST | Create a new user |
/users/{id}/ | GET | Get one single user |
/users/{id}/ | PATCH | Partially update a user |
/users/{id}/ | DELETE | Remove a user |
Django: Focuses on building HTML pages that are rendered by the server, along with forms and templates. It is great for traditional web apps where the server creates the whole page itself. Django’s views and forms are built around this request-response cycle.
DRF: Focuses on building APIs that return structured data (usually JSON) for clients like single-page apps, mobile apps, or other services to use. DRF gives you serializers for validating and converting data, and views that handle API-specific things like authentication and permissions.
Use Django views and templates when you are building a single, all-in-one web app where the server creates and sends the HTML pages directly to users.
Use DRF when you need to expose an API for frontend frameworks (React, Vue), mobile apps, or other services to talk to. DRF lets you keep a clean separation between your backend logic and how clients use your data.
Django and DRF each have their own request and response objects. DRF’s Request builds on top of Django’s HttpRequest and adds extra features for reading request data, handling authentication, and managing content negotiation. In the same way, DRF’s Response builds on top of Django’s HttpResponse and makes it easier to send back JSON responses with the right status code and content type.
To use DRF’s Request and Response, you usually import them from rest_framework and use them inside your API views. For example:
# views.pyfrom rest_framework.views import APIViewfrom 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)api_view DecoratorThe @api_view decorator is a function from DRF that lets you create function-based API views. You give it a list of HTTP methods (for example, ['GET', 'POST']) that the view should respond to. When you use @api_view, DRF automatically wraps your function so it can properly handle API requests and responses, including content negotiation, authentication, and permission checks.
By default, an api view only accepts the GET method. So if you want to allow other methods like POST or PATCH, you need to list them inside 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 passstatus ModuleThe status module in DRF gives you a set of named constants for HTTP status codes. Instead of trying to remember number codes like 200, 404, or 500, you can use clear names like status.HTTP_200_OK, status.HTTP_404_NOT_FOUND, and status.HTTP_500_INTERNAL_SERVER_ERROR. This makes your code easier to read and makes it obvious what each response status actually means.
When sending back a response in DRF, you can use the status module to set the right status code. For example:
from rest_framework import statusfrom rest_framework.response import Responsedef my_api_view(request): data = {"message": "Hello, DRF!"} return Response(data, status=status.HTTP_200_OK)