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/memust be declared before/users/{user_id}. - Unbounded
limitparameters 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.