> ## Documentation Index
> Fetch the complete documentation index at: https://genai.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Request Handling

> Master Path Parameters, Query Parameters, Request Bodies, and Headers in FastAPI.

When building APIs, clients need to send data to the server. FastAPI provides clean, standard mechanisms to handle incoming data through:

* **Path Parameters**: Identifying a specific resource.
* **Query Parameters**: Filtering or sorting resources.
* **Request Bodies**: Sending complex payloads (JSON) to create or update resources.
* **Headers**: Sending metadata (like auth credentials).

***

## 1. Request Data Processing Flow

FastAPI intercepts incoming HTTP requests, extracts parameters, validates their data types using Pydantic, and feeds them directly into your Python route function.

```mermaid theme={null}
graph TD
    Client[Client HTTP Request] --> Route{FastAPI Router}
    
    Route -->|Path Params: /employees/42| PathVal[Verify integer type]
    Route -->|Query Params: ?dept=Sales| QueryVal[Verify string type]
    Route -->|Request Body: JSON payload| BodyVal[Validate with Pydantic Schema]
    
    PathVal & QueryVal & BodyVal -->|Validation Passes| Controller[Execute Route Function]
    PathVal & QueryVal & BodyVal -->|Validation Fails| Error[Return 422 Unprocessable Entity]
```

***

## 2. Path Parameters

Path parameters are variables embedded directly inside the URL path. They are typically used to point to a specific, unique resource.

### Example: Fetching a Specific Employee

Let's add an endpoint to retrieve a single employee using their unique `employee_id`:

```python theme={null}
from fastapi import FastAPI

app = FastAPI()

# Sample mock database
EMPLOYEES = {
    1: {"name": "Alice", "department": "Engineering"},
    2: {"name": "Bob", "department": "Product"}
}

@app.get("/employees/{employee_id}")
def get_employee(employee_id: int):
    # FastAPI automatically validates that 'employee_id' is an integer
    employee = EMPLOYEES.get(employee_id)
    if not employee:
        return {"error": "Employee not found"}
    return employee
```

### Key Takeaways

* **Syntax**: Define the variable in the path inside curly braces: `/employees/{employee_id}`.
* **Type Safety**: Annotate `employee_id: int` in the function arguments. If a user requests `/employees/abc`, FastAPI immediately returns a `422 Unprocessable Entity` error explaining that `employee_id` must be an integer, saving you from writing manual type-checking code!

***

## 3. Query Parameters

Any function parameter that is **not** part of the path is automatically treated as a query parameter. Query parameters appear after the `?` in the URL (e.g., `/employees?department=Product&role=Manager`).

### Example: Filtering Employees

Let's add search and filtering functionality to the employee list endpoint:

```python theme={null}
@app.get("/employees")
def list_employees(department: str | None = None, limit: int = 10):
    # 'department' is optional (defaults to None)
    # 'limit' is an optional integer (defaults to 10)
    results = list(EMPLOYEES.values())
    
    if department:
        results = [emp for emp in results if emp["department"].lower() == department.lower()]
        
    return results[:limit]
```

### Key Takeaways

* **Optional parameters**: Use `str | None = None` to denote an optional query parameter.
* **Default values**: Provide a default value directly (e.g., `limit: int = 10`).

***

## 4. Request Bodies with Pydantic

When you need to send structured data to create or update a resource, you should use a **Request Body** via an HTTP `POST`, `PUT`, or `PATCH` request. You define the shape of this body using a **Pydantic Model**.

### Example: Creating (Onboarding) an Employee

First, define the schema, then declare the path operation parameter:

```python theme={null}
from pydantic import BaseModel, Field

# 1. Define the Pydantic Schema
class EmployeeCreate(BaseModel):
    name: str = Field(..., min_length=2, description="First and last name of the employee")
    department: str = Field(..., description="Assigned department")
    salary: float = Field(..., gt=0, description="Monthly base salary")

@app.post("/employees")
def create_employee(employee: EmployeeCreate):
    # FastAPI automatically parses JSON body and validates against EmployeeCreate schema
    new_id = max(EMPLOYEES.keys()) + 1 if EMPLOYEES else 1
    EMPLOYEES[new_id] = employee.model_dump()
    return {"id": new_id, "data": EMPLOYEES[new_id]}
```

### Key Takeaways

* FastAPI automatically reads the body as JSON, validates it against `EmployeeCreate`, and injects it as an object named `employee` into the function.
* You can access the validation data using `.model_dump()` to get a standard Python dictionary.

***

## 5. Headers & Metadata

You can read headers sent by clients (e.g., API keys, user-agent details, system configuration metrics) using the `Header` class from FastAPI.

### Example: Reading an API Key or Client User-Agent

```python theme={null}
from fastapi import Header

@app.get("/system-info")
def get_system_info(user_agent: str | None = Header(None), x_api_key: str | None = Header(None)):
    return {
        "user_agent": user_agent,
        "api_key_sent": x_api_key is not None
    }
```

> \[!NOTE]
> FastAPI automatically converts snake\_case arguments (like `x_api_key`) to match kebab-case headers (like `X-API-Key`) sent by the client.

## Request Validation

FastAPI automatically validates incoming data.

```python theme={null}
from pydantic import BaseModel, Field

class Student(BaseModel):
    name: str
    age: int = Field(gt=0)
    course: str
```

Invalid request:

```json theme={null}
{
    "name": "Rahul",
    "age": -5,
    "course": "CSE"
}
```

If validation fails, FastAPI returns a **422 Validation Error** without executing the route.

## Data Validation can be applied to:

* **Request Body** using `Field()`
* **Query Parameters** using `Query()`
* **Path Parameters** using `Path()`

In **Pydantic v2**, the recommended way to specify validations is using Python's `Annotated` type.

### What is `Annotated`?

`Annotated` lets you combine a **data type** with **validation metadata**.

**Syntax**

```python theme={null}
Annotated[type, validation]
```

For example:

```python theme={null}
from typing import Annotated
from pydantic import Field

age: Annotated[int, Field(gt=0, lt=100)]
```

Here:

* `int` specifies the expected data type.
* `Field(gt=0, lt=100)` specifies the validation rules.

The equivalent (older) syntax is:

```python theme={null}
age: int = Field(gt=0, lt=100)
```

Both styles work, but **`Annotated` is the recommended approach** in FastAPI and Pydantic v2.

### 1. `Field()` – Request Body Validation

`Field()` is used inside **Pydantic models** to validate request body fields.

**Recommended**

```python theme={null}
from typing import Annotated
from pydantic import BaseModel, EmailStr, Field

class Student(BaseModel):
    name: Annotated[str, Field(min_length=3, max_length=50)]
    age: Annotated[int, Field(gt=0, lt=100)]
    email: EmailStr
    cgpa: Annotated[float, Field(ge=0, le=10)]
```

**Alternative**

```python theme={null}
class Student(BaseModel):
    name: str = Field(min_length=3, max_length=50)
    age: int = Field(gt=0, lt=100)
    email: EmailStr
    cgpa: float = Field(ge=0, le=10)
```

### 2.`Query()` – Query Parameter Validation

`Query()` validates values passed as query parameters.

Example request:

```http theme={null}
GET /students?page=1&limit=20&dept=CSE
```

**Recommended**

```python theme={null}
from typing import Annotated
from fastapi import Query

@app.get("/students")
def get_students(
    page: Annotated[int, Query(ge=1)] = 1,
    limit: Annotated[int, Query(ge=1, le=100)] = 20,
    department: Annotated[str | None, Query(alias="dept")] = None,
):
    ...
```

**Alternative**

```python theme={null}
@app.get("/students")
def get_students(
    page: int = Query(default=1, ge=1),
    limit: int = Query(default=20, ge=1, le=100),
    department: str | None = Query(default=None, alias="dept"),
):
    ...
```

### 3. `Path()` – Path Parameter Validation

`Path()` validates values passed as path parameters.

Example request:

```http theme={null}
GET /students/101
```

**Recommended**

```python theme={null}
from typing import Annotated
from fastapi import Path

@app.get("/students/{student_id}")
def get_student(
    student_id: Annotated[int, Path(gt=0)]
):
    ...
```

**Alternative**

```python theme={null}
@app.get("/students/{student_id}")
def get_student(
    student_id: int = Path(gt=0)
):
    ...
```

### Common Validation Options

| Option        | Purpose                        |
| ------------- | ------------------------------ |
| `gt`          | Greater than                   |
| `ge`          | Greater than or equal          |
| `lt`          | Less than                      |
| `le`          | Less than or equal             |
| `min_length`  | Minimum string length          |
| `max_length`  | Maximum string length          |
| `pattern`     | Regular expression             |
| `default`     | Default value                  |
| `alias`       | Alternate field/parameter name |
| `title`       | Display title in API docs      |
| `description` | Description in API docs        |
| `examples`    | Example values                 |

### Common Pydantic Types

| Type       | Purpose                   |
| ---------- | ------------------------- |
| `EmailStr` | Validates email addresses |
| `AnyUrl`   | Validates URLs            |
| `UUID`     | Validates UUID values     |
| `date`     | Date                      |
| `datetime` | Date and time             |
| `Decimal`  | High-precision decimal    |

Example:

```python theme={null}
from pydantic import BaseModel, EmailStr

class Student(BaseModel):
    email: EmailStr
```

If an invalid email is provided, FastAPI automatically returns a **422 Unprocessable Entity** response.

> **Note:** `EmailStr` requires the `email-validator` package.

```bash theme={null}
pip install email-validator
```
