8.3. Path Parameters
Table of Contents
Why Path Parameters Matter
In a web API, URLs are more than just addresses. They also carry information.
For example:
/users/1/products/12345/orders/2024/08/27
The changing parts like 1, 12345, 2024 are path parameters.
In FastAPI, path parameters let you define dynamic routes where part of the URL is a variable.
Without path parameters, you would need a different route for every user or product, which is impossible.
Path parameters = parts of the URL path that act as variables and are required for the route to match.
Basic Path Parameters in FastAPI
Defining a simple path parameter
Start with a minimal FastAPI app:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}Explanation:
"/items/{item_id}"has{item_id}as a path parameter.item_id: intin the function makes FastAPI:- Convert the value from the URL to an integer.
- Validate that the value is an integer.
- If you request
/items/10, the response is:
{"item_id": 10}
If you try /items/abc, FastAPI returns a 422 Unprocessable Entity error, because abc is not an integer.
FastAPI uses Python type hints on path parameters for automatic validation and conversion.
Path parameter vs query parameter
To see the difference:
@app.get("/users/{user_id}")
def read_user(user_id: int, details: bool = False):
return {"user_id": user_id, "details": details}user_idis a path parameter. It comes from the URL part{user_id}.detailsis a query parameter. It comes from?details=true.
Examples:
/users/5returns{"user_id": 5, "details": false}/users/5?details=truereturns{"user_id": 5, "details": true}
Path parameters are part of the path and are required.
Query parameters are after ? and are usually optional.
Multiple and Typed Path Parameters
Multiple path parameters
You can have more than one path parameter:
@app.get("/users/{user_id}/orders/{order_id}")
def read_user_order(user_id: int, order_id: int):
return {"user_id": user_id, "order_id": order_id}
Request: /users/3/orders/99
Response:
{"user_id": 3, "order_id": 99}Different types for path parameters
You can use any type that FastAPI can handle. Common ones:
| Type | Example URL | Python value |
|---|---|---|
str | /items/book | "book" |
int | /items/10 | 10 |
float | /prices/9.99 | 9.99 |
bool | /feature/true | True |
UUID | /files/uuid-here | UUID object |
date | /reports/2024-08-27 | date object |
Example with different types:
from datetime import date
from uuid import UUID
@app.get("/reports/{report_date}")
def read_report(report_date: date):
return {"report_date": report_date}
@app.get("/files/{file_id}")
def read_file(file_id: UUID):
return {"file_id": file_id}Requests:
/reports/2024-08-27gives{"report_date": "2024-08-27"}/files/550e8400-e29b-41d4-a716-446655440000gives a valid UUID as JSON
Path Order and Routing Rules
Static vs dynamic paths
FastAPI decides which function to call based on the path patterns.
Consider:
@app.get("/users/me")
def read_current_user():
return {"user": "current user"}
@app.get("/users/{user_id}")
def read_user(user_id: int):
return {"user_id": user_id}Requests:
/users/megoes toread_current_user./users/42goes toread_userwithuser_id = 42.
Important detail: static paths are matched before dynamic paths.
So /users/me is not captured by /users/{user_id}.
If you only had:
@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}
Then /users/me would match this route, with user_id = "me".
Path parameter name and function argument name
The name inside {} must match exactly the function parameter name.
Correct:
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}Incorrect:
@app.get("/items/{item_id}")
def read_item(id: int): # Does NOT match
return {"id": id}
FastAPI will raise an error at startup, because it cannot find a function parameter named item_id.
Path parameter name in the URL and function parameter name must be identical.
Path Converters and Validation
Path parameter types using colon syntax
FastAPI builds on Starlette and supports type conversion inside the path string:
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
You usually rely on the function type hint item_id: int.
There is also a Starlette-style path converter syntax, but in FastAPI you almost always use Python types only.
Keep to:
@app.get("/items/{item_id}")
def read_item(item_id: int): ...
rather than Django-style path("items/<int:item_id>/"). FastAPI does not use that pattern.
Validating path parameters with Path()
You can add extra constraints with Path:
from fastapi import Path
@app.get("/items/{item_id}")
def read_item(
item_id: int = Path(
..., ge=1, le=1000, description="The ID must be between 1 and 1000"
)
):
return {"item_id": item_id}...means the parameter is required.ge=1means greater than or equal to 1.le=1000means less than or equal to 1000.
Examples:
/items/10works./items/0returns validation error because 0 < 1./items/1500returns validation error because 1500 > 1000.
Common numeric constraints:
| Argument | Meaning |
|---|---|
gt | greater than |
ge | greater than or equal |
lt | less than |
le | less than or equal |
You can also enforce a minimum or maximum length on string parameters:
@app.get("/users/{username}")
def read_user(
username: str = Path(..., min_length=3, max_length=20, regex="^[a-zA-Z0-9_]+$")
):
return {"username": username}Here:
min_length=3,max_length=20regex="^[a-zA-Z0-9_]+$"only allows letters, numbers, and underscores.
Request /users/ab will fail with a 422 error, because it is too short.
Documenting path parameters
Path also lets you add descriptions, titles, and examples that show up in the OpenAPI docs:
@app.get("/products/{product_id}")
def read_product(
product_id: int = Path(
...,
title="Product ID",
description="The numeric ID of the product to retrieve",
example=123,
)
):
return {"product_id": product_id}
When you visit /docs, you will see this extra information for the product_id parameter.
Special Path Segment: `path` Type
Sometimes you want a path parameter that includes slashes, such as a file path:
/files/home/user/report.txt/images/2024/08/photo.png
Normal path parameters stop at the next /. To include slashes inside a parameter, use the special path converter:
@app.get("/files/{file_path:path}")
def read_file(file_path: str):
return {"file_path": file_path}Requests:
/files/home/user/report.txtgives{"file_path": "home/user/report.txt"}/files/images/2024/08/photo.pnggives{"file_path": "images/2024/08/photo.png"}
Without :path, FastAPI would try to match different segments separately and would not treat the whole rest as one variable.
Use {name:path} when the parameter must capture multiple segments with slashes.
Practical Design Examples with Path Parameters
Example 1: Basic blog URLs
Imagine a simple blog API:
@app.get("/posts/{post_id}")
def get_post(post_id: int):
return {"post_id": post_id}
@app.get("/users/{user_id}/posts")
def list_user_posts(user_id: int):
return {"user_id": user_id, "posts": []}
@app.get("/users/{user_id}/posts/{post_id}")
def get_user_post(user_id: int, post_id: int):
return {"user_id": user_id, "post_id": post_id}Paths:
/posts/10= specific post/users/5/posts= list all posts of user 5/users/5/posts/10= user 5's post 10
Example 2: Products with category and product code
@app.get("/categories/{category_name}/products/{product_code}")
def get_category_product(category_name: str, product_code: str):
return {
"category": category_name,
"product_code": product_code,
}Requests:
/categories/electronics/products/TV123/categories/books/products/ISBN-978-3-16-148410-0
Example 3: Date-based resources
Using date conversion:
from datetime import date
@app.get("/reports/{year}/{month}/{day}")
def daily_report(year: int, month: int, day: int):
return {"date": f"{year:04d}-{month:02d}-{day:02d}"}
@app.get("/reports/{report_date}")
def daily_report_date(report_date: date):
return {"date": report_date}Two styles:
- Separate integers for year, month, day:
/reports/2024/08/27 - Single
datetype:/reports/2024-08-27
Common Pitfalls and Tips
Pitfall 1: Wrong parameter name
Wrong:
@app.get("/orders/{order_id}")
def get_order(id: int):
return {"id": id}
FastAPI error at startup, because {order_id} has no matching function parameter.
Fix:
@app.get("/orders/{order_id}")
def get_order(order_id: int):
return {"order_id": order_id}Pitfall 2: Conflicting routes
Example:
@app.get("/items/{item_id}")
def get_item(item_id: int): ...
@app.get("/items/special")
def get_special_item(): ...
This works, because /items/special is static and will be matched before /items/{item_id}.
But if you reverse them, it still works, because FastAPI still gives priority to the more specific path pattern. The important point is to avoid ambiguous patterns, such as:
@app.get("/files/{name}")
def get_file(name: str): ...
@app.get("/files/{file_path:path}")
def get_file_path(file_path: str): ...
Here, /files/a/b clearly matches the path version, but /files/test is ambiguous conceptually. FastAPI will still pick one, but this can be confusing. Avoid overlapping patterns when possible.
Pitfall 3: Using path parameters where query parameters are better
If a value is optional or more like a filter, use a query parameter, not a path parameter.
Better:
- Path parameter for the resource identity, for example
/users/10 - Query parameters for filters, for example
/users?country=US&page=2
Not recommended:
/users/country/US/page/2for filters
Tip: Combine validation and documentation
Use Path to combine validation and docs in a clear way:
from fastapi import Path
@app.get("/orders/{order_id}")
def get_order(
order_id: int = Path(
...,
ge=1,
description="Positive integer ID of the order",
example=42,
)
):
return {"order_id": order_id}Summary
- Path parameters make parts of your URL dynamic.
- They are required and come from the URL path, not from the query string.
- The name in
{}must match the function argument name. - Use Python type hints to convert and validate values automatically.
- Use
Pathto add constraints likege,le, length, and regex, and to document parameters. - Use
{name:path}when a parameter must include slashes. - Design routes so that static paths and dynamic paths do not conflict in confusing ways.
These basics of path parameters are essential for building clear and predictable FastAPI APIs.
Views: 8
KAHIBARO