Skip to content

Chapter 3 · Path Parameters and Query Parameters ​

Chapter goals: Master path parameter type conversion and validation, declare optional/required/list query parameters, and understand FastAPI's recognition rules and common pitfalls when mixing both.

3.1 Path Parameters and Auto Type Conversion ​

Parts of the path wrapped in {} are path parameters. FastAPI parses and validates them based on the function signature's type annotations:

python
from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
def read_item(item_id: int):
    # Annotated as int: request to /items/5 auto-converts;
    # request to /items/abc returns a 422 validation error and never enters the function
    return {"item_id": item_id}

A request to /items/abc returns a structured error (HTTP 422):

json
{
  "detail": [
    {
      "type": "int_parsing",
      "loc": ["path", "item_id"],
      "msg": "Input should be a valid integer, unable to parse string as an integer"
    }
  ]
}

This is "types are validation": every type annotation you write is simultaneously a runtime guard and a documentation statement.

3.2 Constraining Values with Enum ​

To restrict a path parameter to a fixed set of values, use an Enum:

python
from enum import Enum
from fastapi import FastAPI

app = FastAPI()


class ModelName(str, Enum):
    # Inherit from str so members participate in string comparison and serialization
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"


@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {"model": model_name, "message": "Deep Learning FTW!"}
    return {"model": model_name, "message": "Have some residuals"}

Visiting /models/resnet works normally; /models/gpt4 returns 422 with the valid values listed in the error. The docs page renders it as a dropdown.

3.3 Declaration Order Pitfall (Review and Reinforce) ​

Chapter 2 covered route matching order; here we look at it from the parameter perspective:

python
from fastapi import FastAPI

app = FastAPI()

# ✅ Static path first
@app.get("/files/latest")
def latest_file():
    return {"file": "report-2026.pdf"}


@app.get("/files/{file_path:path}")
def read_file(file_path: str):
    # :path converter allows the parameter to include slashes, matching multi-level paths
    return {"file_path": file_path}

The :path converter is a Starlette feature — /files/a/b/c.txt gives file_path="a/b/c.txt", while a plain str parameter would 404 on slashes. Even so, /files/latest must still be declared first, or it is never reached.

3.4 Query Parameters ​

Anything after ?key=value&... in the URL is a query parameter. Any scalar parameter in the function signature that is not a path parameter is automatically treated as a query parameter:

python
from typing import Annotated
from fastapi import FastAPI

app = FastAPI()


@app.get("/items/")
def read_items(
    skip: int = 0,            # Has default → optional query param, defaults to 0
    limit: int = 10,
    q: str | None = None,     # Optional string, can be None
):
    # GET /items/?skip=20&limit=5&q=apple
    return {"skip": skip, "limit": limit, "q": q}


@app.get("/search")
def strict_search(keyword: str):
    # No default → required query param; missing it returns 422
    return {"keyword": keyword}

Three rules to remember:

  1. Parameter name appears in the path template → path parameter;
  2. Parameter is a scalar type (int/str/bool/float, etc.) → query parameter;
  3. Parameter is a Pydantic model → request body (Chapter 4).

3.5 Chapter Summary ​

  • Path parameters: type annotation drives auto-conversion and validation; wrong type → 422;
  • Enum path params render as dropdowns in /docs;
  • Static paths must be declared before parameterized ones;
  • Query params: scalar with default = optional; scalar without default = required;
  • Pydantic model parameter = request body (next chapter).

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. What HTTP status code does FastAPI return when a path parameter fails type validation?

2. How do you make a query parameter optional?

3. Why must /files/latest be declared before /files/{file_path:path}?

4. What determines whether a function parameter is treated as a path param, query param, or body?

🛠️ Hands-On Exercises ​

  1. Write a route GET /products/{product_id} where product_id is validated as an integer greater than 0; test with both valid and invalid IDs.
  2. Add query params skip (default 0), limit (default 10), and an optional category: str | None = None; test with various combinations.
  3. Create an Enum Status(str, Enum) with values active/inactive/archived; use it as a path param and verify the /docs dropdown renders all three options.

Proceed to Chapter 4: Request Body and Pydantic Models.