Routing and Endpoints
Learn how to create and manage routes and endpoints effectively in FastAPI applications.
Content
Path Parameters
Versions:
Watch & Learn
AI-discovered learning video
Sign in to watch the learning video for this topic.
Path Parameters in FastAPI — The Sexy Bits of Your URL
"URLs are not just addresses. They're tiny contracts between client and server. Path parameters are the handshake."
You're past the baby steps: you know how to run a FastAPI server, and you've written your first endpoint. Next up: making your routes actually useful. Path parameters let parts of the URL carry data — the VIPs of routing. This guide builds on the stuff you already saw in "Defining Routes" and your "First FastAPI Application," so we won't repeat server bootstrapping. We're going straight into the good stuff.
Why path parameters even exist (and why they matter)
- Paths capture identity in the URL: /items/42, /users/alice, /files/images/cat.png.
- They make APIs RESTy, predictable, and human-readable.
- FastAPI converts path values into Python types (with validation!) and documents everything automatically in OpenAPI — so your docs get smarter without extra work.
Think of path parameters like the variable parts of a parking permit: one spot is always "/user/{id}", but the numbers change.
Basic usage: the classic example
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
- The name inside
{}becomes the function parameter name. - The type hint (
int) tells FastAPI to convert and validate the path param. If the client sends/items/fooyou'll get a 422 validation error. - Path parameters are always required — they are part of the path, not an optional decoration.
Path vs Query: quick table
| Where it lives | Example URL | How it's used | Required? |
|---|---|---|---|
| Path parameter | /users/123 |
Identifies a resource | Yes |
| Query parameter | /users?limit=10 |
Filters, sorts, options | No (usually) |
Hint: If it identifies something, use a path parameter. If it modifies behavior, use a query param.
Validation and metadata using Path()
Want to say "this ID must be > 0" or provide a description that shows up in docs? Use fastapi.Path.
from fastapi import Path
@app.get("/items/{item_id}")
def read_item(item_id: int = Path(..., title="Item ID", gt=0, description="The ID must be positive")):
return {"item_id": item_id}
...marks the parameter as required (path params are inherently required, but this keeps docs explicit).- You can use
gt,ge,lt,le,regexanddescriptionto enrich validation and docs.
Order matters: put fixed routes before dynamic ones
This is a common gotcha. Suppose you want both /users/me and /users/{user_id}:
@app.get("/users/me")
def read_me():
return {"user": "the current user"}
@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}
If you define /users/{user_id} first, me might be consumed as a user_id. Put the specific route first so FastAPI matches it before the catch-all parameter.
Path types beyond str and int
FastAPI supports Python types out of the box. Examples:
from uuid import UUID
@app.get("/orders/{order_id}")
def read_order(order_id: UUID):
return {"order_id": order_id}
- Send a valid UUID and FastAPI parses it into a
UUIDobject. - If the value doesn't match, the client sees a validation error (422).
You can also use float, bool (with caution), and even custom types with Pydantic.
Capturing paths that contain slashes (the ":path" trick)
Sometimes you need to accept a file path like /files/images/2022/cat.png. Use the :path converter in the route:
@app.get("/files/{file_path:path}")
def read_file(file_path: str):
return {"file_path": file_path}
Now file_path will include slashes — like a greedy gobbler.
Regex validation for path parameters
If you want pattern matching: use Path(..., regex=...).
@app.get("/color/{hex_value}")
def read_color(hex_value: str = Path(..., regex=r"^[0-9A-Fa-f]{6}$", description="6-hex-digit color")):
return {"hex": hex_value}
This gives precise control over what the path accepts and shows that control in the docs.
Combining path and query parameters (real-world example)
@app.get("/items/{item_id}")
def item_detail(item_id: int, q: str | None = None, short: bool = False):
result = {"item_id": item_id}
if q:
result["q"] = q
if not short:
result["description"] = "Lots of juicy details here..."
return result
Path pinpoints the item; query customizes what you want back. That's REST with manners.
Common pitfalls and troubleshooting
- You get 422 even though the route exists. Likely a type mismatch (e.g., sending letters to an
intparam). /users/mereturns the same as/users/123. You defined the parameterized route before the static one.- Trying to make a path param optional. You can't. If the segment is in the URL pattern, it must be present.
Quick cheatsheet (because we love TL;DRs)
- Use
{name}in the route andnamein the function signature. - Type hints = automatic parsing + automatic docs.
- Use
Path(...)for validation and OpenAPI descriptions. - Put specific routes before generic
{param}routes. - Use
{p:path}to capture slashes. - Use
UUIDtype for strong identity matching.
Final flourish — why this is powerful
FastAPI turns path parameters from boilerplate into expressive, validated, documented API contracts. You get robust endpoints with minimal code and excellent dev ergonomics. Plus your OpenAPI docs show exactly what your API expects — no guessing, no defensive parsing code, and fewer bugs.
Path parameters are like the secret handshake of your API: small, explicit, and utterly required to get inside.
Go forth: make your routes precise, your validations meaningful, and your docs something you're proud to show off. And next up — we can talk about request bodies, dependency injection, or how to gracefully version your routes. Pick your adventure.
Comments (0)
Please sign in to leave a comment.
No comments yet. Be the first to comment!