Request Handling & Validation
Mastering data intake, Pydantic models, and automatic validation in FastAPI.
Request Handling Fundamentals
In FastAPI, request handling is the comprehensive cycle of receiving data from a client, validating it against defined schemas, processing it through business logic, and returning an appropriate response.
Types of Request Data
FastAPI provides native support for every standard way a client can send data:
| Type | Description / Example |
|---|---|
| Path Params | Embedded directly in URL: /users/10 |
| Query Params | Optional URL suffixes: /users?age=20 |
| Request Body | JSON payload for complex data objects (POST/PUT) |
| Headers | Metadata like Auth tokens or User-Agent |
| Cookies | Browser-persistent session data storage |
| Form Data | Traditional forms and automated file uploads |
Parameters & Request Body
Path Parameters (Validation)
FastAPI enforces types automatically through Python type hints. If the wrong type is provided (e.g., a string for an integer ID), it triggers a 422 Unprocessable Entity error instantly.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def get_item(item_id: int):
return {"item_id": item_id}Query Parameters (Advanced)
FastAPI handles default values and optional parameters seamlessly. Use Optional from the typing module for truly optional fields.
@app.get("/products")
def get_products(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
# Optional Query Parameters
from typing import Optional
@app.get("/search")
def search(q: Optional[str] = None):
return {"query": q}Request Body (JSON)
Crucial for POST and PUT requests. Pydantic models define the expected structure of the JSON payload.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
name: str
age: int
@app.post("/users")
def create_user(user: User):
return userPydantic Deep Dive
What is Pydantic?
Pydantic is the library that FastAPI uses for all data validation and parsing. It ensures that the incoming data exactly matches the expected schema before your function logic ever runs.
Field Validation Constraints
The Field class provides powerful constraints like length limits, numeric ranges, and regex matching.
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(..., min_length=3, max_length=50)
age: int = Field(..., gt=0, lt=120)Complex Schemas & Logic
Nested Models & Collections
Pydantic models can be nested to represent complex real-world data structures like addresses within user profiles.
from typing import List, Optional
class Address(BaseModel):
city: str
zip: str
class User(BaseModel):
name: str
is_active: bool = True # Default Value
age: Optional[int] = None
address: Address # Nested Model Component
hobbies: List[str] # Complex collectionCustom Validation (Validators)
For logic that standard constraints can't cover, use the @validator decorator.
from pydantic import BaseModel, validator
class User(BaseModel):
name: str
@validator("name")
def name_must_be_alpha(cls, value):
if not value.isalpha():
raise ValueError("Only alphabets allowed")
return valueSpecialized Request Data
Headers & Form Data
Access metadata like authentication tokens or perform traditional form submissions.
from fastapi import Header, Form
@app.get("/headers")
def get_headers(user_agent: str = Header(None)):
return {"User-Agent": user_agent}
@app.post("/login")
def login(username: str = Form(...), password: str = Form(...)):
return {"username": username}📂 File Uploads
Handling binary data and metadata for file uploads efficiently using UploadFile.
from fastapi import File, UploadFile
@app.post("/upload")
def upload(file: UploadFile = File(...)):
return {"filename": file.filename}Control & Dependencies
1. Response Model Filtering
The response_model parameter ensures you never accidentally leak sensitive data like hashed passwords back to the client.
class UserResponse(BaseModel):
name: str
@app.post("/users", response_model=UserResponse)
def create_user(user: User):
return user #age/password is automatically filtered out2. Dependency Injection (Intro)
FastAPI's dependency injection system allows for highly reusable and testable logic (like shared DB connections or authentication guards).
from fastapi import Depends
def common_params(q: str = None):
return q
@app.get("/items")
def read_items(q: str = Depends(common_params)):
return {"q": q}Interview Preparation
🔥 Theory Questions
What is the primary role of Pydantic in a FastAPI project?▼
What status code is returned on validation failure and why?▼
Explain the difference between Query and Path parameters.▼
Why would you use a response_model?▼
How does FastAPI handle Dependency Injection?▼
💻 Coding Challenges
Validate age between 18 and 60 (inclusive)
from pydantic import BaseModel, Field
class User(BaseModel):
age: int = Field(..., ge=18, le=60)Construct a highly nested API model structure
class Address(BaseModel):
city: str
class User(BaseModel):
name: str
address: Address
@app.post("/user")
def create_profile(user: User):
return userImplement a Custom Validator for email safety
from pydantic import BaseModel, validator
class User(BaseModel):
email: str
@validator("email")
def validate_secure_email(cls, val):
if "@" not in val:
raise ValueError("Invalid email formatting")
return valHigh-Level Insights
Why this module is CRITICAL?
Nearly 70% of backend engineeringrevolves around receiving and validating input. Whether it's complex Authentication, ML model inputs, or high-stakes Database schemas, validation is your primary line of defense.
The Lazy Approach
Handling user input with raw, unvalidated dictionaries leads to unexpected crashes and security holes.
The Engineering Standard
Always leverage typed Pydantic models to guarantee data safety before any business logic executes.