Django
JSON
web development
request handling
Python

Where's my JSON data in my incoming Django request?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In plain Django, incoming JSON is usually not in request.POST. It is in the raw request body, which means you need to read request.body and decode it yourself, unless you are using Django REST Framework, which provides a higher-level request parser.

Why request.POST Is Empty

request.POST is designed for form-encoded or multipart form submissions. If the client sends:

text
Content-Type: application/json

then Django does not automatically place that payload into request.POST. Instead, the raw bytes are available at request.body.

That distinction matters because many developers assume any POST request should populate request.POST. That is only true for form-style payloads.

Read JSON from request.body

In a regular Django view, the standard pattern is:

python
1import json
2from django.http import JsonResponse
3
4
5def create_item(request):
6    if request.method != "POST":
7        return JsonResponse({"error": "POST required"}, status=405)
8
9    try:
10        payload = json.loads(request.body.decode("utf-8"))
11    except json.JSONDecodeError:
12        return JsonResponse({"error": "invalid JSON"}, status=400)
13
14    name = payload.get("name")
15    quantity = payload.get("quantity", 0)
16
17    return JsonResponse({
18        "name": name,
19        "quantity": quantity,
20    })

This works because request.body gives you the raw bytes from the HTTP body. You decode those bytes and then parse the JSON string.

Confirm the Client Sends the Right Headers

If the client says it is sending JSON but uses the wrong content type, debugging becomes messy. Make sure the client sends application/json.

Example with fetch:

javascript
1fetch("/items/create/", {
2  method: "POST",
3  headers: {
4    "Content-Type": "application/json",
5    "X-CSRFToken": csrftoken
6  },
7  body: JSON.stringify({
8    name: "widget",
9    quantity: 3
10  })
11});

If the request body is JSON but the header says application/x-www-form-urlencoded, then your server-side assumptions and your client-side behavior are out of sync.

Django REST Framework Is Different

If you are using Django REST Framework, the request object usually exposes parsed content via request.data. That includes JSON payloads.

python
1from rest_framework.decorators import api_view
2from rest_framework.response import Response
3
4
5@api_view(["POST"])
6def create_item_api(request):
7    name = request.data.get("name")
8    quantity = request.data.get("quantity", 0)
9    return Response({
10        "name": name,
11        "quantity": quantity,
12    })

This is simpler because DRF handles content negotiation and parsing for you. The important point is that plain Django and DRF do not expose the request body the same way.

Handle Validation Early

Parsing JSON is not the same as validating it. Once you have a dictionary, you still need to confirm required fields, types, and allowed values.

python
1import json
2from django.http import JsonResponse
3
4
5def create_user(request):
6    try:
7        payload = json.loads(request.body.decode("utf-8"))
8    except json.JSONDecodeError:
9        return JsonResponse({"error": "invalid JSON"}, status=400)
10
11    username = payload.get("username")
12    age = payload.get("age")
13
14    if not isinstance(username, str) or not username.strip():
15        return JsonResponse({"error": "username is required"}, status=400)
16
17    if not isinstance(age, int) or age < 0:
18        return JsonResponse({"error": "age must be a non-negative integer"}, status=400)
19
20    return JsonResponse({"ok": True})

Treat malformed JSON and invalid application data as two separate failure cases.

Debugging the Raw Request

When a request does not parse as expected, log the method, content type, and body size before changing the view logic.

python
1def debug_request(request):
2    return JsonResponse({
3        "method": request.method,
4        "content_type": request.content_type,
5        "body_size": len(request.body),
6    })

That often reveals the real problem quickly:

  • the client sent form data, not JSON
  • the body is empty
  • the content type is wrong
  • the request was not a POST at all

Small facts beat guesswork here.

Common Pitfalls

The most common mistake is reading JSON from request.POST. For JSON requests, that object is often empty because the body is not form-encoded.

Another common issue is using valid JSON in the client but forgetting the Content-Type: application/json header. That makes server-side handling inconsistent and harder to diagnose.

A third problem is assuming parsing equals validation. A JSON body can be syntactically valid and still be unusable for your application.

Summary

  • In plain Django, JSON request data usually lives in request.body, not request.POST.
  • Decode request.body and parse it with json.loads.
  • Make sure the client sends Content-Type: application/json.
  • Use request.data only when working with Django REST Framework.
  • Separate JSON parsing errors from application-level validation errors.

Course illustration
Course illustration

All Rights Reserved.