What is a Request Object?
The Request object in FastAPI represents the HTTP request sent by the client. It contains metadata and payload(the actual data that the client sends to the server), such as:
- HTTP method (
GET, POST, PUT, DELTE, OPTIONS, PATCH, etc) - Query parameters
- Request headers
- Cookies
- Request body
- Client IP
- Path parameters
- App context (shared state)
Key Attributes of Request
| Attribute | Description |
|---|
request.method | HTTP method used for the request (e.g., GET, POST, PUT, DELETE, OPTIONS) |
request.url | Full URL of the incoming request (e.g., http://localhost:9248/ds2man/basic/task/?question=Hello&user_name=DS2Man) |
request.base_url | Base URL excluding the path and query string (e.g., http://localhost:9248/) |
request.headers | All HTTP headers (case-insensitive multi-dict) |
request.query_params | Query parameters parsed as a dictionary-like object (e.g., question=Hello&user_name=DS2Man) |
request.path_params | Path parameters extracted from the route (e.g., /items/{item_id} → {"item_id": 123}) |
request.cookies | Cookies included in the request, as a dictionary |
request.client | Client’s IP address and port, e.g., (e.g., Address(host=’127.0.0.1’, port=10519) |
request.scope | Low-level ASGI connection information (type, headers, path, etc.) - used internally by Starlette |
request.state | Per-request mutable state for sharing data between middlewares and routes (e.g., request.state.user) |
await request.body() | Entire request body as raw bytes |
await request.json() | Parsed JSON body as a dictionary |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| @router.get("/request&response/case1")
async def debug_request(request: Request):
visible_attrs = []
for key in dir(request):
if key.startswith("_"): # Exclude attributes starting with __ (e.g., __len__)
continue
try:
value = getattr(request, key)
# callable: Checks whether the object can be called like a function
if not callable(value): # Exclude methods/functions
visible_attrs.append(key)
except Exception:
# Skip attributes like request.auth that may raise errors when accessed
continue
result = {
"visible_keys": visible_attrs
}
return result
|
1
2
3
4
5
6
7
8
9
| url = "http://127.0.0.1:7249/ds2man/basic/request&response/case1"
async with aiohttp.ClientSession(
trust_env=True, timeout=aiohttp.ClientTimeout(total=120)
) as session:
async with session.get(url) as response:
data = await response.json()
print(f"\nJSON Body:")
print(f"type(data): {type(data)}")
pprint(data)
|
1
2
3
| JSON Body:
type(data): <class 'dict'>
{'visible_keys': ['base_url', 'client', 'cookies', 'headers', 'method', 'path_params', 'query_params', 'scope', 'state', 'url']}
|
Example - Using GET
1
2
3
4
5
6
7
8
9
10
| @router.get("/request&response/case2")
async def get_request_info(request: Request):
result = {
"method": request.method,
"client": request.client.host,
"headers": dict(request.headers),
"query": dict(request.query_params)
}
return result
|
1
2
3
4
5
6
7
8
9
| url = "http://127.0.0.1:7249/ds2man/basic/request&response/case2"
async with aiohttp.ClientSession(
trust_env=True, timeout=aiohttp.ClientTimeout(total=120)
) as session:
async with session.get(url) as response:
data = await response.json()
print(f"\nJSON Body:")
print(f"type(data): {type(data)}")
pprint(data)
|
1
2
3
4
5
6
7
8
9
| JSON Body:
type(data): <class 'dict'>
{'client': '127.0.0.1',
'headers': {'accept': '*/*',
'accept-encoding': 'gzip, deflate',
'host': '127.0.0.1:7249',
'user-agent': 'Python/3.11 aiohttp/3.11.11'},
'method': 'GET',
'query': {}}
|
Example - Using POST
1
2
3
4
5
6
7
8
9
10
11
12
| @router.post("/request&response/case3")
async def get_request_info(request: Request):
data = await request.json()
result = {
"method": request.method,
"client": request.client.host,
"headers": dict(request.headers),
"query": dict(request.query_params),
"body": data
}
return result
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| url = "http://127.0.0.1:7249/ds2man/basic/request&response/case3"
payload = {
"key1": "value1",
"key2": "value2"
}
async with aiohttp.ClientSession(
trust_env=True, timeout=aiohttp.ClientTimeout(total=120)
) as session:
async with session.post(url, json=payload) as response:
data = await response.json()
print(f"\nJSON Body:")
print(f"type(data): {type(data)}")
pprint(data)
|
1
2
3
4
5
6
7
8
9
10
11
12
| JSON Body:
type(data): <class 'dict'>
{'body': {'key1': 'value1', 'key2': 'value2'},
'client': '127.0.0.1',
'headers': {'accept': '*/*',
'accept-encoding': 'gzip, deflate',
'content-length': '36',
'content-type': 'application/json',
'host': '127.0.0.1:7249',
'user-agent': 'Python/3.11 aiohttp/3.11.11'},
'method': 'POST',
'query': {}}
|
What is a Response Object?
A Response object defines what the API sends back to the client. FastAPI handles most responses automatically by serializing the returned values, but for more control (e.g., setting custom headers, content type, or cookies), you can use the Response object directly.
FastAPI uses JSONResponse by default to automatically convert responses into JSON. Additionally, it provides other response classes such as HTMLResponse and RedirectResponse, which will be covered in detail in a later post.
There are several types of responses:
JSONResponse (default)HTMLResponsePlainTextResponseStreamingResponseRedirectResponseFileResponse
1
2
3
4
5
6
7
8
| @router.get("/request&response/case4")
def set_custom_header(response: Response):
response.headers["X-Custom-Header"] = "FastAPI Rocks"
result = {
"message": "Header set"
}
return result
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| url = "http://127.0.0.1:7249/ds2man/basic/request&response/case4"
payload = {
"key1": "value1",
"key2": "value2"
}
async with aiohttp.ClientSession(
trust_env=True, timeout=aiohttp.ClientTimeout(total=120)
) as session:
async with session.get(url) as response:
print(f"Headers:")
pprint(dict(response.headers))
data = await response.json()
print(f"\nJSON Body:")
print(f"type(data): {type(data)}")
pprint(data)
|
1
2
3
4
5
6
7
8
9
10
11
| Headers:
{'Content-Length': '24',
'Content-Type': 'application/json',
'Date': 'Tue, 06 May 2025 00:02:57 GMT',
'Server': 'uvicorn',
'x-custom-header': 'FastAPI Rocks' # adding
}
JSON Body:
type(data): <class 'dict'>
{'message': 'Header set'}
|
Using Request and Response Together
Combining both Request and Response allows for complete control over the full request-response lifecycle:
1
2
3
4
5
6
7
8
9
| @router.post("/request&response/case5")
async def echo(request: Request, response: Response):
data = await request.json()
response.headers["X-Echoed"] = "True"
result = {
"received": data
}
return result
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| url = "http://127.0.0.1:7249/ds2man/basic/request&response/case5"
payload = {
"key1": "value1",
"key2": "value2"
}
async with aiohttp.ClientSession(
trust_env=True, timeout=aiohttp.ClientTimeout(total=120)
) as session:
async with session.post(url, json=payload) as response:
print(f"Headers:")
pprint(dict(response.headers))
data = await response.json()
print(f"\nJSON Body:")
print(f"type(data): {type(data)}")
pprint(data)
|
1
2
3
4
5
6
7
8
9
10
11
| Headers:
{'Content-Length': '46',
'Content-Type': 'application/json',
'Date': 'Tue, 06 May 2025 00:08:40 GMT',
'Server': 'uvicorn',
'x-echoed': 'True' # adding
}
JSON Body:
type(data): <class 'dict'>
{'received': {'key1': 'value1', 'key2': 'value2'}}
|
Summary
| Feature | Request | Response |
|---|
| Origin | starlette.requests.Request | starlette.responses.Response |
| Use case | Access request data from client | Customize what is sent to client |
| Typical fields | .method, .headers, .body() | .headers, .set_cookie(), etc. |
| Customizable | Yes (headers, body, method, etc.) | Yes (headers, cookies, status code) |