While building APIs you must distinguish "values that identify the resource" from "optional filters." Path parameters are embedded in the URL such as /items/10; they are required and specify which entity you want. Query parameters appear as ?sort=latest and add options such as sorting, search keywords, or pagination. A quick mental model: GET /items/10?highlight=price fetches item 10 (path) and optionally toggles how it should be displayed (query). Separating them keeps URLs readable and lets FastAPI mark required fields automatically. Using the same server from part 2, this post demonstrates how to declare and verify both kinds of parameters.
Key terms
- Path parameter – the required piece inside the URL pattern
/items/{id}; this post starts with it. - Query parameter – optional
?key=valuepairs for sorting or filtering. - 422 response – FastAPI’s automatic "validation failed" status when values are missing or typed incorrectly.
Practice card
- Estimated time: 35 minutes
- Prereqs: FastAPI starter app from part 2
- Goal: declare path and query parameters and inspect them in Swagger UI
What this post covers
- Declare path parameters and watch FastAPI convert types
- Mark query parameters as required or optional
- Practice with
uv run fastapi dev main.pyand compare both in Swagger UI
Understanding path parameters: defining the resource
Path parameters live inside the URL structure and are required.
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
Type hints such as int trigger automatic conversion; passing a non-number yields a 422 explaining the error (the response body will point to {"loc": ["path", "item_id"], "msg": "Input should be a valid integer"}).
Make sure the variable in the decorator ({item_id}) matches the function argument name; FastAPI raises an error if they differ.
Understanding query parameters: adding options
Query parameters appear as ?key=value and typically control filters or pagination.
@app.get("/items")
def list_items(q: str | None = None, limit: int = 10):
base_items = ["pencil", "pen", "notebook", "eraser"]
filtered = [item for item in base_items if not q or q.lower() in item.lower()]
return filtered[:limit]
- Setting
q’s default toNonemakes it optional. - Giving
limita default keeps responses capped at 10 unless specified. - Remove the default entirely (e.g.,
q: str) to make a query parameter required—the moment you provide a default, FastAPI treats it as optional in both code and documentation.
Python version tip: the
str | Nonesyntax requires Python 3.10+. If you are on 3.9 or earlier, useOptional[str] = Nonefrom thetypingmodule.
FastAPI interprets any function argument that does not appear in the path template as a query parameter unless you wrap it in Body, Header, or similar helpers.
Example: study team search
Use a path parameter for the team name and a query parameter for sorting.
@app.get("/teams/{team_name}")
def get_team(team_name: str, sort: str = "latest"):
data = fetch_team(team_name) # pretend function that returns {"members": [...]}
order_key = "created_at" if sort == "latest" else "score"
return sorted(data["members"], key=lambda member: member[order_key], reverse=True)
Requests like GET /teams/racing?sort=score immediately reveal what is required and what is optional; Swagger UI shows the same distinction.
Spot the difference in Swagger UI
Run the server:
uv run fastapi dev main.py
Visit http://127.0.0.1:8000/docs in your browser. GET /items/{item_id} displays a Path Parameters section while GET /items renders a Query Params section. The UI alone teaches where each value belongs.
Command fallback: if
fastapi devis unavailable, runuv run uvicorn main:app --reloadinstead; both commands expose the same/docspage.
Add validation rules
Use helpers such as Query to enforce rules.
from fastapi import Query
@app.get("/search")
def search_items(keyword: str = Query(min_length=2, max_length=20)):
return {"keyword": keyword}
Swagger surfaces those constraints automatically, and invalid input returns a 422 detailing the issue.
Recap
- Path parameters are mandatory pieces of the URL pattern and must use the same name in the decorator and function.
- Query parameters come after
?and become optional as soon as you provide a default value. - Type hints and helpers like
Queryboth validate input and document it.
Why the separation matters
Clear roles mean clients know which values are required and which are optional. URLs stay shareable and cacheable, and repeating the same request becomes easier because the critical parts stay in the path.
Security reminder: everything in the path or query string is logged and visible in browser history, so avoid sending secrets or passwords as parameters—use headers or encrypted bodies instead.
Practice
- Follow along: implement
/items/{item_id}and/items, then call each route in Swagger UI. - Extend: add
GET /searchwithmin_lengthand confirm the 422 when only one character is provided. - Debug: intentionally send the wrong type and explain the 422 payload.
- Done when: you can verbally describe the difference between path/query params and both endpoints return 200/422 as expected.
- Bonus: repeat the same requests with
curlorhttpxto reinforce how to build URLs manually.
Wrap-up
Distinguishing path and query parameters instantly clarifies your API design. Next we will accept JSON request bodies and validate them with Pydantic models.
💬 댓글
이 글에 대한 의견을 남겨주세요