DRF: Consuming the API

Four lessons have built up TaskFlow's API from the inside — serializers, viewsets, permissions, filtering. This last DRF lesson looks at it from the other side: what it actually looks like for something outside Django entirely to call that API and use what comes back.

DRF's Browsable API

One of DRF's nicer touches, and something that requires zero extra setup: visiting an API endpoint directly in a regular web browser doesn't just dump raw JSON at you. DRF detects that the request came from a browser and renders a clean, human-friendly HTML page instead — showing the JSON nicely formatted, along with forms for trying a POST or PUT right there in the page, and links for paging through results. Visiting /api/tasks/ in a browser during development is a genuinely fast way to sanity-check an endpoint without reaching for a separate tool at all — it comes from DRF automatically the moment a ViewSet or APIView is wired up, with nothing extra to configure.

Calling the API With curl

A plain GET request needs nothing special — no session, no cookie, since the endpoint is read-only for anonymous requests under the IsAuthenticatedOrReadOnly permission from the previous lesson:

curl http://localhost:8000/api/tasks/

Creating a task requires a valid token, sent in the same Authorization header covered in DRF: Authentication & Permissions, along with a JSON body describing the new task:

curl -X POST http://localhost:8000/api/tasks/ \
  -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4" \
  -H "Content-Type: application/json" \
  -d '{"title": "Draft the release notes", "due_date": "2026-09-01", "priority": "medium"}'

-H attaches each header, and -d supplies the request body — this single command reaches TaskFlow's API exactly as a real client would, with no browser involved at all.

Calling the API From Python With requests

curl is great for a quick check; a real client is usually code. Python's requests library is a natural way to call TaskFlow's API from a completely separate script — this is exactly the shape any automation, data pipeline, or integration script would take:

# A separate script, entirely outside the Django project
import requests

headers = {'Authorization': 'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4'}

response = requests.get('http://localhost:8000/api/tasks/', headers=headers)
data = response.json()
print(data['count'], 'tasks total')

for task in data['results']:
    print(task['title'], task['priority'])

# Creating a task follows the same shape as the curl example above
new_task = {'title': 'Draft the release notes', 'due_date': '2026-09-01', 'priority': 'medium'}
response = requests.post('http://localhost:8000/api/tasks/', json=new_task, headers=headers)
print(response.status_code, response.json())

Notice response.json() parses the paginated body from the previous lesson straight back into a Python dict — data['count'] and data['results'] work exactly like the JSON keys shown there. There's nothing Django-specific about this script at all; it would run identically against any server speaking the same API.

The Same Idea, From a Real Frontend

A separate JavaScript frontend — React, Vue, or a mobile app — talks to a Django backend exactly this way: it sends HTTP requests with fetch or axios instead of Python's requests, but the API itself, the tokens, the JSON shapes, and the pagination and filtering behavior are all identical from the server's point of view. This is the essence of a headless architecture — Django never renders a template at all here, it's purely a JSON API, and something else entirely owns the user interface. This site's own React Native track covers the client-side half of exactly this pattern, in its coverage of fetch and axios — everything built across these five DRF lessons is what a screen in that track would ultimately be calling.

Documenting the API: OpenAPI/Swagger

An API with a couple of endpoints is easy to remember by heart; one with dozens quickly isn't. Packages like drf-spectacular and drf-yasg generate an OpenAPI (formerly known as Swagger) schema automatically, straight from a project's existing serializers and views — no separate documentation to write and keep in sync by hand. The result is an interactive page listing every endpoint, its expected request body, and its possible responses, that other developers (or a future you) can browse without reading the view code directly. This is conceptual awareness worth having going in: it's not something TaskFlow needs with just a handful of endpoints, but it becomes genuinely valuable the moment an API grows past what one person can hold in their head.

Note: The curl and requests commands above are correct examples of calling a real DRF API — but this website's own project doesn't run a live Django server, so none of these requests actually go anywhere from this page.
Next up: With the API built and callable, the track turns to making sure all of TaskFlow's code — API included — keeps working as it grows: Testing: Unit Tests.