6.7 JSON Requests
Table of Contents
Understanding JSON Requests
When you build backend APIs, JSON requests are the most common way clients send data to your server. In this chapter you learn what JSON is in the context of HTTP, how clients send JSON, and how your backend should read, validate, and respond to JSON requests.
What Is JSON in HTTP Requests?
JSON stands for JavaScript Object Notation. It is a simple text format for representing structured data.
A JSON document is always text. For example:
{
"name": "Alice",
"age": 30,
"is_admin": false,
"skills": ["python", "sql"],
"profile": {
"twitter": "@alice",
"website": "https://example.com"
}
}In HTTP, a JSON request means:
- The client sends a normal HTTP request.
- The request body contains JSON text.
- The
Content-Typeheader says that the body is JSON.
For example, a raw HTTP request:
POST /users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 102
{
"name": "Alice",
"email": "alice@example.com",
"age": 30
}Your backend must:
- Read the bytes in the request body.
- Interpret them as text.
- Parse that text as JSON into data structures, for example a dictionary or list.
- Use that data in your application logic.
Content-Type: application/json
The Content-Type header tells your server how to interpret the request body.
For JSON requests, it must be:
Content-Type: application/jsonSome clients also use:
Content-Type: application/json; charset=utf-8Your backend should treat both as JSON.
Important rule
For JSON requests, always send
Content-Type: application/json in the request,
and on the server never try to parse a body as JSON if the content type does not indicate JSON.
If the header is missing or wrong, typical backend behavior:
- Try to parse JSON anyway and return
400 Bad Requestif parsing fails, or - Return
415 Unsupported Media Typeto say the server does not accept this content type.
Example of a 415 response:
HTTP/1.1 415 Unsupported Media Type
Content-Type: application/json
{
"detail": "Content-Type application/xml is not supported. Use application/json."
}JSON vs Other Request Body Types
In the previous chapter on form data you saw how HTML forms usually send data.
Here is how JSON compares to two common alternatives:
| Feature | JSON (application/json) | Form URL encoded (application/x-www-form-urlencoded) | Multipart form (multipart/form-data) |
|---|---|---|---|
| Best for | APIs, structured data, nested objects | Simple form submissions, small key-value pairs | File uploads, mixed text and files |
| Body format | JSON text | key=value&key2=value2 | Binary with boundaries |
| Nested objects | Easy, natural | Possible but awkward | Possible, but more complex |
| File uploads | Not good, needs base64 or separate endpoint | Not suitable | Designed for this |
| Client side (JS) | JSON.stringify(obj) | URLSearchParams or FormData | FormData |
For REST APIs:
- Use JSON for most request and response bodies.
- Use multipart form for file uploads.
Typical Use Cases for JSON Requests
JSON requests are used whenever you want to send structured data in an API:
- Creating a resource.
POST /users
Content-Type: application/json
{
"name": "Alice",
"email": "alice@example.com",
"password": "secret123"
}- Updating a resource.
PUT /users/123
Content-Type: application/json
{
"name": "Alice Doe",
"email": "alice.doe@example.com"
}- Partially updating a resource.
PATCH /users/123
Content-Type: application/json
{
"email": "alice.new@example.com"
}- Filtering in a complex way, when query parameters are not enough.
POST /search
Content-Type: application/json
{
"keywords": ["backend", "python"],
"price_range": { "min": 10, "max": 100 },
"sort_by": "created_at",
"order": "desc"
}Valid vs Invalid JSON
JSON is strict. Small formatting errors make it invalid.
Valid JSON examples
{
"name": "Alice",
"age": 30,
"is_admin": false,
"skills": ["python", "sql"],
"address": null
}[1, 2, 3, 4]{"value": 12.5}Invalid JSON examples
{
name: "Alice" // keys must be in double quotes
}{
"age": 30, // trailing comma not allowed
}{
"skills": ['python'] // strings must use double quotes, not single
}
If a client sends invalid JSON in a request, your backend should respond with a 400 Bad Request and a clear error message.
Example:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"detail": "Invalid JSON in request body"
}Reading JSON Requests in a Simple Python Server
You will use frameworks like FastAPI later, but it is useful to see what happens under the hood.
Imagine a very simple HTTP server built with Python standard library. Reading a JSON request body looks like this:
import json
def handle_request(env):
# env is a dict-like object containing request info
# 1. Check Content-Type
content_type = env.get("CONTENT_TYPE", "")
if "application/json" not in content_type:
return 415, {"error": "Unsupported Media Type. Use application/json."}
# 2. Read Content-Length
try:
length = int(env.get("CONTENT_LENGTH", 0))
except ValueError:
length = 0
# 3. Read raw body bytes
body_bytes = env["wsgi.input"].read(length)
# 4. Decode to string
body_str = body_bytes.decode("utf-8")
# 5. Parse JSON
try:
data = json.loads(body_str)
except json.JSONDecodeError:
return 400, {"error": "Invalid JSON"}
# Now data is a Python dict or list
# Example logic:
name = data.get("name")
return 200, {"message": f"Hello, {name}!"}Key steps:
- Check
Content-Type. - Read the correct number of bytes.
- Decode bytes to string using UTF 8.
- Use a JSON parser to convert string to objects.
- Handle parse errors.
JSON Request Examples with curl
curl is a command line tool to send HTTP requests. It is very useful to test JSON requests.
Sending JSON with POST
curl -X POST "http://localhost:8000/users" \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'Explanation:
-X POSTsets the HTTP method.-Hadds a header.-dsets the request body as text.
Clean formatting using single quotes and pretty JSON
On Unix shells:
curl -X POST "http://localhost:8000/users" \
-H "Content-Type: application/json" \
-d '{
"name": "Alice",
"email": "alice@example.com",
"age": 30
}'On Windows PowerShell you often need double quotes outside and single quotes inside:
curl -Method POST "http://localhost:8000/users" `
-Headers @{ "Content-Type" = "application/json" } `
-Body '{"name": "Alice", "email": "alice@example.com", "age": 30}'Common mistake
If you forget the Content-Type header:
curl -X POST "http://localhost:8000/users" \
-d '{"name": "Alice"}'
Some servers will treat this as application/x-www-form-urlencoded and not as JSON. The body may not be parsed the way you expect.
JSON Requests in JavaScript (Fetch API)
Frontend applications often send JSON to your backend using fetch in the browser.
Sending JSON with fetch
const user = {
name: "Alice",
email: "alice@example.com"
};
fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(user)
})
.then(response => response.json())
.then(data => {
console.log("Server response:", data);
})
.catch(error => {
console.error("Error:", error);
});Important parts:
headers["Content-Type"] = "application/json"body: JSON.stringify(user)converts a JavaScript object to JSON text.
Reading JSON response in JavaScript
Most JSON APIs also respond with JSON:
fetch("https://api.example.com/users/123")
.then(response => {
if (!response.ok) {
throw new Error("Request failed: " + response.status);
}
return response.json(); // parse JSON response body
})
.then(user => {
console.log("User:", user.name);
})
.catch(error => {
console.error("Error:", error);
});Designing JSON Request Payloads
When you design an API, you decide what JSON structure clients must send.
Some principles:
- Use clear and consistent field names.
- Use appropriate data types.
- Use nested objects when it makes sense.
- Avoid sending unnecessary data.
- Separate input models from output models when needed.
Example: Creating a blog post
You might define the JSON request body for creating a blog post as:
{
"title": "My first post",
"content": "Hello backend world",
"tags": ["backend", "learning"],
"published": false
}You can document this in human language:
title: string, required, 3 to 100 characters.content: string, required, up to 10,000 characters.tags: array of strings, optional, max 10 items.published: boolean, optional, default false.
Later, in validation chapters and in REST API documentation chapters you will see how to describe this more formally.
Validating JSON Request Bodies
After you parse JSON, you must validate it. Validation checks if the incoming data matches what your application expects.
Example: Manual validation in Python
import json
def create_user(env):
# Read and parse JSON, simplified
body = env["wsgi.input"].read(int(env.get("CONTENT_LENGTH", 0)))
try:
data = json.loads(body.decode("utf-8"))
except json.JSONDecodeError:
return 400, {"detail": "Invalid JSON"}
errors = []
# Check required fields
if "email" not in data:
errors.append({"field": "email", "message": "Email is required"})
if "password" not in data:
errors.append({"field": "password", "message": "Password is required"})
# Type checks
if "email" in data and not isinstance(data["email"], str):
errors.append({"field": "email", "message": "Email must be a string"})
if "age" in data and not isinstance(data["age"], int):
errors.append({"field": "age", "message": "Age must be an integer"})
if errors:
return 422, {"detail": errors}
# Use data safely now
# ...
return 201, {"message": "User created"}Here:
- JSON structure is parsed.
- Fields are checked for existence.
- Types are verified.
- Errors are collected and returned with status code
422 Unprocessable Entity.
You will later use libraries and frameworks to avoid doing all of this manually.
Nested JSON Objects and Arrays
JSON supports nested structures. Your API can accept complex request bodies.
Example: Order creation request
{
"customer": {
"name": "Alice",
"email": "alice@example.com"
},
"items": [
{
"product_id": 1,
"quantity": 2
},
{
"product_id": 5,
"quantity": 1
}
],
"shipping_address": {
"line1": "123 Main St",
"city": "Exampleville",
"country": "US"
},
"notes": "Leave at the front door"
}On the backend side, after parsing, this becomes a nested structure, for example in Python:
order["customer"]["name"]
order["items"][0]["product_id"]
order["shipping_address"]["city"]When you design JSON, try to match the logical structure of the data in your system.
Handling Empty or Missing JSON Bodies
Sometimes a request with method POST, PUT, or PATCH might come with:
- No body.
- An empty string body.
- An empty JSON object
{}.
You should clearly define what your API expects.
Common patterns
- For endpoints that require data, return
400 Bad Requestor422 Unprocessable Entityif the body is missing or empty. - For endpoints where all fields are optional, accept
{}and use defaults.
Example:
PATCH /users/123
Content-Type: application/json
{}If your API allows partial updates where all fields are optional, this might be valid and simply do nothing.
JSON and Character Encoding
JSON over HTTP almost always uses UTF 8 encoding.
- Request header might say:
Content-Type: application/json; charset=utf-8. - The backend decodes bytes as UTF 8 to get text.
- JSON can contain non ASCII characters.
Example JSON with non ASCII:
{
"name": "José",
"message": "こんにちは"
}Your backend should:
- Always decode JSON request bodies as UTF 8.
- Return responses with
Content-Type: application/json; charset=utf-8.
Common JSON Request Mistakes and How to Handle Them
1. Missing Content-Type
Request:
POST /users
Content-Length: 34
{"name": "Alice", "email": "alice@example.com"}Server should respond with something like:
HTTP/1.1 415 Unsupported Media Type
Content-Type: application/json
{
"detail": "Content-Type application/json is required"
}2. Invalid JSON syntax
Request body:
{ "name": "Alice", }
Trailing comma makes it invalid. Respond with 400 Bad Request and message about invalid JSON.
3. Wrong data types
Request:
{
"name": "Alice",
"age": "30" // should be a number
}
Respond with 422 Unprocessable Entity and an error describing the invalid field.
4. Unexpected fields
Request:
{
"username": "alice", // API expects "name", not "username"
"email": "alice@example.com",
"extra": "something"
}Depending on your API rules, you can:
- Ignore unknown fields, or
- Reject payload with a clear error.
It is better to be strict for public APIs.
JSON Requests and Idempotent Methods
You will learn more about HTTP methods and REST in later chapters. For JSON requests:
POSTwith JSON usually creates a resource, not idempotent.PUTwith JSON usually replaces a resource, idempotent.PATCHwith JSON usually partially updates a resource, not guaranteed idempotent.
Example PUT with JSON:
PUT /users/123
Content-Type: application/json
{
"name": "Alice",
"email": "alice@example.com"
}If you send this same JSON multiple times, the final state is the same as if you sent it once. That is idempotent behavior.
JSON Request Size and Limits
JSON requests can be large. Most servers and frameworks allow you to set a maximum body size.
- Protects your server from memory issues.
- Prevents abuse, for example very large payloads.
Example policy:
- Reject JSON request bodies larger than 1 MB with status
413 Payload Too Large.
Response example:
HTTP/1.1 413 Payload Too Large
Content-Type: application/json
{
"detail": "Request body too large, max 1 MB"
}Summary
- JSON requests are HTTP requests where the body contains JSON text and the
Content-Typeisapplication/json. - Your backend needs to:
- Check and respect the
Content-Typeheader. - Read and decode the request body.
- Parse JSON and handle syntax errors.
- Validate the parsed data against what your API expects.
- Return clear error responses when something is wrong.
- JSON supports nested objects and arrays, which lets you design expressive request payloads.
- Clients, such as browsers and command line tools, send JSON by setting the proper header and converting objects to JSON text.
Understanding JSON requests is essential for building modern APIs. In the next chapters you will connect this knowledge with response handling, templates, and full request response flows in a web framework.
Views: 9
KAHIBARO