>_
EngineeringNotes
← Back to FastAPI & Python
Module 02

Request Handling & Validation

Mastering data intake, Pydantic models, and automatic validation in FastAPI.

01

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:

TypeDescription / Example
Path ParamsEmbedded directly in URL: /users/10
Query ParamsOptional URL suffixes: /users?age=20
Request BodyJSON payload for complex data objects (POST/PUT)
HeadersMetadata like Auth tokens or User-Agent
CookiesBrowser-persistent session data storage
Form DataTraditional forms and automated file uploads
02

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.

Path Params Example
python
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.

Query Params & Optionality
python
@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.

JSON Request Body
python
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 user
03

Pydantic 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.

Automatic validation
Clear, actionable error messages
Data parsing (e.g., string "12" → int 12)
Native Async/Typed core

Field Validation Constraints

The Field class provides powerful constraints like length limits, numeric ranges, and regex matching.

Field Implementation
python
from pydantic import BaseModel, Field

class User(BaseModel):
    name: str = Field(..., min_length=3, max_length=50)
    age: int = Field(..., gt=0, lt=120)
min_lengthmax_lengthgt / lt (greater/less than)ge / le (greater/less or equal)regex
04

Complex Schemas & Logic

Nested Models & Collections

Pydantic models can be nested to represent complex real-world data structures like addresses within user profiles.

Structured Models
python
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 collection

Custom Validation (Validators)

For logic that standard constraints can't cover, use the @validator decorator.

Custom Validator Logic
python
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 value
05

Specialized Request Data

Headers & Form Data

Access metadata like authentication tokens or perform traditional form submissions.

Metadata & Forms
python
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.

File Upload Endpoint
python
from fastapi import File, UploadFile

@app.post("/upload")
def upload(file: UploadFile = File(...)):
    return {"filename": file.filename}
06

Control & Dependencies

1. Response Model Filtering

The response_model parameter ensures you never accidentally leak sensitive data like hashed passwords back to the client.

Secure Response Schema
python
class UserResponse(BaseModel):
    name: str

@app.post("/users", response_model=UserResponse)
def create_user(user: User):
    return user #age/password is automatically filtered out

2. Dependency Injection (Intro)

FastAPI's dependency injection system allows for highly reusable and testable logic (like shared DB connections or authentication guards).

Dependency Example
python
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}
07

Interview Preparation

🔥 Theory Questions

What is the primary role of Pydantic in a FastAPI project?
Answer:Pydantic performs data validation through Python type hints, transforming incoming JSON into structured objects and ensuring data integrity before logic execution.
What status code is returned on validation failure and why?
Answer:FastAPI returns a 422 Unprocessable Entity code. This signifies that the request format was correct (valid JSON), but the content failed validation rules.
Explain the difference between Query and Path parameters.
Answer:Path parameters are positional and required component of the URL (/users/{id}). Query parameters are name-value pairs appended at the end (/users?sort=desc) and are typically optional.
Why would you use a response_model?
Answer:To decouple internal data structures from the external API representation, preventing sensitive field leaks and providing a clean, validated interface for clients.
How does FastAPI handle Dependency Injection?
Answer:By using the Depends() class in function signatures, FastAPI automatically resolves and executes dependent logic, injecting the returned value into the route handler.

💻 Coding Challenges

Challenge 1
Validate age between 18 and 60 (inclusive)
Age Constraint
python
from pydantic import BaseModel, Field

class User(BaseModel):
    age: int = Field(..., ge=18, le=60)
Challenge 2
Construct a highly nested API model structure
Nested Logic
python
class Address(BaseModel):
    city: str

class User(BaseModel):
    name: str
    address: Address

@app.post("/user")
def create_profile(user: User):
    return user
Challenge 3
Implement a Custom Validator for email safety
Deep Validation
python
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 val
08

High-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.

def create_user(user: dict):
The Engineering Standard

Always leverage typed Pydantic models to guarantee data safety before any business logic executes.

def create_user(user: User):