FastAPI: From Basics to Production · FastAPI Basics

Path and Query Parameters

Declare, validate and document URL parameters with Annotated.

Function parameters that appear in the path template are path parameters; other simple-typed parameters become query parameters. Use Annotated with Path() and Query() to add validation (ge, le, min_length, pattern) and documentation. Optional query parameters default to None; enums restrict values; lists (list[str]) accept repeated parameters.

from enum import Enum
from typing import Annotated
from fastapi import FastAPI, Path, Query

app = FastAPI()


class SortOrder(str, Enum):
    newest = "newest"
    price = "price"


@app.get("/categories/{slug}/products")
async def list_products(
    slug: Annotated[str, Path(pattern=r"^[a-z0-9-]+$")],
    q: Annotated[str | None, Query(max_length=100)] = None,
    tags: Annotated[list[str], Query()] = [],           # ?tags=a&tags=b
    sort: SortOrder = SortOrder.newest,
    limit: Annotated[int, Query(ge=1, le=100)] = 20,
    offset: Annotated[int, Query(ge=0)] = 0,
):
    return {"slug": slug, "q": q, "tags": tags, "sort": sort, "limit": limit, "offset": offset}

Common pitfalls

  • Route order: /users/me must be declared before /users/{user_id}.
  • Unbounded limit parameters letting clients request millions of rows.
  • Using Optional[str] without a default — it's still required.

Key points

  • Annotated + Path/Query for validation
  • Enums restrict values
  • Always bound pagination parameters

Further reading: FastAPI: Query parameters and validations

Practice questions

Why must /users/me be declared before /users/{user_id}?

Answer

Routes are matched in order; if the parameterized route comes first, 'me' is captured as user_id (and fails int validation).

How do you accept ?tag=a&tag=b in FastAPI?

Answer

Declare tag: Annotated[list[str], Query()] = []; FastAPI collects repeated query parameters into a list.

Open in the app to mark lessons complete, take notes and track your progress.