Skip to main content

FastAPI Request Objects

Introduction

When building APIs with FastAPI, understanding how to work with request objects is essential. The Request object provides access to the underlying HTTP request data, giving you control over how your API interacts with clients. In this guide, we'll explore FastAPI's Request objects in depth, learning how to access request data, headers, cookies, and more.

A Request object represents the HTTP request sent by the client to your API. It contains all the information about that request, including:

  • The HTTP method (GET, POST, PUT, etc.)
  • Headers sent by the client
  • Query parameters
  • Request body
  • Client information (IP address, etc.)
  • Cookies

Let's dive in and see how we can harness the power of Request objects in FastAPI!

Importing the Request Object

To work with Request objects in FastAPI, you need to import them from the fastapi package:

python
from fastapi import FastAPI, Request

app = FastAPI()

Basic Request Object Usage

The simplest way to work with a Request object is to include it as a parameter in your path operation function:

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.get("/")
async def root(request: Request):
return {"request_url": request.url.path}

In this example, we're accessing the path attribute of the request URL and returning it. If you navigate to the root endpoint (/), you'll get:

json
{
"request_url": "/"
}

Accessing Request Properties

URL and Query Parameters

FastAPI's Request object allows you to access various URL components:

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.get("/items")
async def read_items(request: Request):
url_path = request.url.path
full_url = str(request.url)
query_params = dict(request.query_params)

return {
"url_path": url_path,
"full_url": full_url,
"query_params": query_params
}

If you visit /items?category=books&sort=price, you'll get:

json
{
"url_path": "/items",
"full_url": "http://127.0.0.1:8000/items?category=books&sort=price",
"query_params": {
"category": "books",
"sort": "price"
}
}

Request Headers

Accessing headers is straightforward with the Request object:

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.get("/headers")
async def read_headers(request: Request):
user_agent = request.headers.get("user-agent")
accept_language = request.headers.get("accept-language")
all_headers = dict(request.headers)

return {
"user_agent": user_agent,
"accept_language": accept_language,
"all_headers": all_headers
}

The output will include the headers sent in your request:

json
{
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
"accept_language": "en-US,en;q=0.9",
"all_headers": {
"host": "127.0.0.1:8000",
"connection": "keep-alive",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp...",
"accept-language": "en-US,en;q=0.9"
// ... other headers
}
}

Client Information

You can access client information such as IP address:

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.get("/client")
async def client_info(request: Request):
client_host = request.client.host
client_port = request.client.port

return {
"client_host": client_host,
"client_port": client_port
}

Example response:

json
{
"client_host": "127.0.0.1",
"client_port": 52431
}

Working with Request Body

FastAPI provides multiple ways to access the request body. Here's how to access the raw request body:

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/raw-body")
async def get_raw_body(request: Request):
body = await request.body()

return {
"body_size": len(body),
"body_content": body.decode()
}

If you send a POST request with a JSON body like {"name": "John", "age": 30}, you'll receive:

json
{
"body_size": 28,
"body_content": "{\"name\": \"John\", \"age\": 30}"
}

For JSON content, you can use request.json():

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/json-body")
async def get_json_body(request: Request):
json_data = await request.json()

return {
"received_data": json_data,
"name_from_json": json_data.get("name")
}

Response for the same JSON input:

json
{
"received_data": {
"name": "John",
"age": 30
},
"name_from_json": "John"
}

For form data, use request.form():

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/form-data")
async def get_form_data(request: Request):
form_data = await request.form()

return {
"form_items": dict(form_data)
}

Accessing Cookies

You can access cookies from the request object:

python
from fastapi import FastAPI, Request

app = FastAPI()

@app.get("/cookies")
async def read_cookies(request: Request):
session_cookie = request.cookies.get("session")
all_cookies = request.cookies

return {
"session_cookie": session_cookie,
"all_cookies": all_cookies
}

Real-World Example: Request Logger Middleware

Let's implement a practical example of using Request objects in a middleware to log request details:

python
from fastapi import FastAPI, Request
import time
import logging

# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger("request-logger")

app = FastAPI()

@app.middleware("http")
async def log_requests(request: Request, call_next):
# Get request details
request_id = id(request)
path = request.url.path
method = request.method
client_ip = request.client.host

# Log request start
logger.info(f"Request [{request_id}] {method} {path} started from {client_ip}")

# Process request and time it
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time

# Log request completion
logger.info(
f"Request [{request_id}] {method} {path} completed in {process_time:.4f}s "
f"with status {response.status_code}"
)

return response

@app.get("/")
async def root():
return {"message": "Hello World"}

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
return {"item_id": item_id, "q": q}

When you make requests to your API, you'll see log entries like:

2023-08-15 14:23:45,123 - request-logger - INFO - Request [140371825953232] GET / started from 127.0.0.1
2023-08-15 14:23:45,125 - request-logger - INFO - Request [140371825953232] GET / completed in 0.0021s with status 200
2023-08-15 14:23:50,789 - request-logger - INFO - Request [140371825954512] GET /items/42?q=test started from 127.0.0.1
2023-08-15 14:23:50,791 - request-logger - INFO - Request [140371825954512] GET /items/42?q=test completed in 0.0015s with status 200

This middleware logs each request's details, timing, and outcome—useful for debugging, monitoring, and performance analysis.

Real-World Example: API Key Validation

Here's another practical example showing how to validate API keys in request headers:

python
from fastapi import FastAPI, Request, HTTPException
from typing import Dict

app = FastAPI()

# In a real app, you'd store these in a secure database
API_KEYS: Dict[str, str] = {
"abc123": "user1",
"xyz789": "user2",
"def456": "admin"
}

@app.get("/secure-data")
async def get_secure_data(request: Request):
# Get API key from header
api_key = request.headers.get("X-API-Key")

if not api_key:
raise HTTPException(status_code=401, detail="API Key is missing")

if api_key not in API_KEYS:
raise HTTPException(status_code=403, detail="Invalid API Key")

# Get the user associated with the API key
user = API_KEYS[api_key]

return {
"message": f"Secure data for {user}",
"timestamp": time.time()
}

To test this endpoint, you'd make a request with an API key in the headers:

GET /secure-data
X-API-Key: abc123

Response:

json
{
"message": "Secure data for user1",
"timestamp": 1692106234.567
}

Without a valid API key, you'd receive an error response.

Handling Large Request Bodies

When dealing with large file uploads or request bodies, you can use request.stream() to process the data in chunks:

python
from fastapi import FastAPI, Request
import aiofiles
import os

app = FastAPI()

@app.post("/upload-large-file")
async def upload_large_file(request: Request):
# Create a file to write to
file_path = "uploaded_file.dat"

total_bytes = 0
async with aiofiles.open(file_path, 'wb') as f:
# Process the request body in chunks
async for chunk in request.stream():
total_bytes += len(chunk)
await f.write(chunk)

file_size = os.path.getsize(file_path)

return {
"file_size": file_size,
"total_bytes_received": total_bytes,
"message": "File uploaded successfully"
}

This approach is memory-efficient because it doesn't load the entire file into memory at once.

Summary

FastAPI's Request objects provide a powerful interface for accessing all aspects of incoming HTTP requests. In this guide, we've covered:

  • Accessing basic request properties like URL, path, and query parameters
  • Reading request headers and client information
  • Working with request bodies in various formats
  • Accessing cookies
  • Real-world examples including request logging middleware and API key validation
  • Handling large request bodies efficiently

Understanding Request objects is crucial for building robust APIs that can properly process client data, validate inputs, and implement security features like authentication and authorization.

Additional Resources

Exercises

  1. Create an endpoint that returns all information about the client's browser based on the User-Agent header.
  2. Build a request counter middleware that tracks how many requests each client IP makes.
  3. Implement an endpoint that accepts file uploads and validates file types based on content inspection.
  4. Create a rate limiter middleware that uses request information to limit how many requests a client can make per minute.
  5. Build an endpoint that accepts JSON data and form data in the same request and processes both.

By mastering FastAPI Request objects, you'll be able to build more sophisticated, secure, and user-friendly APIs!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)