Path, Query, and Request Body

Introduction

FastAPI reads HTTP data from path segments, query strings, and JSON bodies—and validates types before your function executes. This chapter covers Path, Query, and Pydantic models as request bodies, the core of every REST endpoint.

Prerequisites

Path Parameters

python
from fastapi import FastAPI
 
app = FastAPI()
 
@app.get("/users/{user_id}")
def read_user(user_id: int):
    return {"user_id": user_id}

URL /users/42user_id == 42.

Invalid type /users/abc422 Unprocessable Entity with JSON detail.

Path Validation

python
from fastapi import FastAPI, Path
 
app = FastAPI()
 
@app.get("/items/{item_id}")
def read_item(
    item_id: int = Path(..., ge=1, le=1000, description="Primary key"),
):
    return {"item_id": item_id}

Code explanation:

  • Path(...) adds constraints and OpenAPI documentation
  • ... means required (Ellipsis)

Order rule: path params have no defaults; put them before query params with defaults.

Query Parameters

Function parameters without path placeholders become query params:

python
@app.get("/items")
def list_items(skip: int = 0, limit: int = 10, q: str | None = None):
    return {"skip": skip, "limit": limit, "q": q}

Request: GET /items?skip=20&limit=5&q=phone

Query Validation

python
from fastapi import Query
 
@app.get("/search")
def search(
    q: str = Query(..., min_length=1, max_length=100),
    page: int = Query(1, ge=1),
):
    return {"q": q, "page": page}

Code explanation:

  • Query(...) marks required query param with validation
  • q: str = None alone makes an optional query param without extra metadata

Boolean query:

python
@app.get("/items/")
def list_items(active: bool = True):
    return {"active_only": active}

/items/?active=false parses correctly.

Request Body (JSON)

Define a Pydantic model (full chapter next):

python
from pydantic import BaseModel, Field
 
class ItemCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=100)
    price: float = Field(..., gt=0)
    tags: list[str] = []
 
@app.post("/items")
def create_item(item: ItemCreate):
    return {"item": item.model_dump(), "saved": True}

Test:

bash
curl -X POST http://127.0.0.1:8000/items \
  -H "Content-Type: application/json" \
  -d '{"name":"Phone","price":699.99,"tags":["electronics"]}'

Code explanation:

  • FastAPI reads JSON body and validates against ItemCreate
  • Validation errors return 422 with field locations

Multiple Parameters Together

Typical pattern: path IDs + optional query filters + JSON body on PUT/PATCH.

Form Data Preview

HTML forms use Form() (not JSON):

python
from fastapi import Form
 
@app.post("/login-form")
def login_form(username: str = Form(), password: str = Form()):
    return {"username": username}

Requires pip install python-multipart. File uploads expand in file uploads chapter.

422 Error Response Shape

Invalid body example response:

json
{
  "detail": [
    {
      "type": "greater_than",
      "loc": ["body", "price"],
      "msg": "Input should be greater than 0",
      "input": -1
    }
  ]
}

Use detail in client error handling and tests.

FAQ

Parameter in path and query same name?

Avoid—use distinct names. Path takes precedence in routing.

Optional body on PUT?

Use ItemUpdate | None = None or separate schemas—see Pydantic chapter.

Array query params?

tags: list[str] = Query()?tags=a&tags=b.

POST data not in body?

Missing Content-Type: application/json or wrong curl -d format.

FastAPI vs Flask request.args?

Flask uses request.args manually; FastAPI declares params in function signature.

Embed multiple body models?

Advanced Body(..., embed=True)—rare; prefer single nested model.