
FastAPI
Advanced path parameter techniques for production APIs.
FastAPI uses Python's type system for validation:
from fastapi import FastAPI
from typing import Optional
import uuid
app = FastAPI()
# Integer path parameter
@app.get("/items/{item_id}")
async def get_item(item_id: int):
# Validates item_id is an integer
# Returns 422 Unprocessable Entity if not
return {"item_id": item_id}
# UUID path parameter
@app.get("/resources/{resource_id}")
async def get_resource(resource_id: uuid.UUID):
# Validates format of UUID
# http://localhost:8000/resources/3fa85f64-5717-4562-b3fc-2c963f66afa6
return {"resource_id": resource_id}
# String with constraints
from pydantic import constr
@app.get("/users/{username}")
async def get_user(username: constr(min_length=3, max_length=50)):
# Validates string length
return {"username": username}from fastapi import Path, HTTPException, status
@app.get("/items/{item_id}")
async def get_item(
item_id: int = Path(..., gt=0, description="Item ID must be positive")
):
'''Get item with validation'''
if item_id > 1000000:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Item ID exceeds maximum allowed value"
)
return {"item_id": item_id}
@app.get("/posts/{post_id}/comments/{comment_id}")
async def get_comment(
post_id: int = Path(..., gt=0, description="Post ID"),
comment_id: int = Path(..., gt=0, description="Comment ID")
):
'''Get comment with multiple validations'''
return {"post_id": post_id, "comment_id": comment_id}Use enums for restricted values:
from enum import Enum
class SortOrder(str, Enum):
ASC = "asc"
DESC = "desc"
class Category(str, Enum):
ELECTRONICS = "electronics"
BOOKS = "books"
CLOTHING = "clothing"
@app.get("/products/{category}")
async def get_products(category: Category):
'''
Only accepts valid categories.
/products/electronics ← Valid
/products/invalid ← Returns 422
'''
if category == Category.ELECTRONICS:
return {"products": ["Laptop", "Phone"]}
elif category == Category.BOOKS:
return {"products": ["Python Guide", "Web Dev"]}
return {"products": []}More specific paths should come before generic ones:
# ✅ Correct order
@app.get("/users/me")
async def get_current_user():
# More specific - comes first
return {"user": "current"}
@app.get("/users/{user_id}")
async def get_user(user_id: int):
# Less specific - comes after
return {"user_id": user_id}
# ❌ Wrong order would break
@app.get("/users/{user_id}")
async def get_user(user_id: int):
# If this comes first, /users/me matches this pattern
return {"user_id": user_id}
@app.get("/users/me")
async def get_current_user():
# Never reached because above pattern matches first
return {"user": "current"}from pathlib import Path as FilePath
@app.get("/files/{file_path:path}")
async def get_file(file_path: str):
'''
The :path syntax allows slashes in the parameter.
/files/documents/2024/report.pdf → file_path = documents/2024/report.pdf
'''
full_path = FilePath("/storage") / file_path
if not full_path.exists():
raise HTTPException(status_code=404, detail="File not found")
return {"file": str(full_path)}from fastapi import Path
@app.get("/users/{user_id}/posts/{post_id}")
async def get_user_post(
user_id: int = Path(
...,
gt=0,
le=999999,
description="The ID of the user. Must be between 1 and 999999"
),
post_id: int = Path(
...,
gt=0,
description="The ID of the post. Must be positive"
)
):
'''
Retrieve a specific post from a specific user.
- **user_id**: Unique identifier for the user (1-999999)
- **post_id**: Unique identifier for the post
'''
return {
"user_id": user_id,
"post_id": post_id,
"content": "Post content here"
}# Cache frequently accessed path parameters
from functools import lru_cache
@lru_cache(maxsize=128)
def get_user_from_cache(user_id: int):
return db.get_user(user_id)
@app.get("/users/{user_id}")
async def get_user(user_id: int):
# Using cache for better performance
user = get_user_from_cache(user_id)
return user
# Validate early to fail fast
@app.get("/items/{item_id}")
async def get_item(
item_id: int = Path(..., gt=0, le=9999999, description="Item ID")
):
'''Early validation prevents database queries for invalid IDs'''
# This validation happens automatically
# Invalid IDs return 422 before reaching this code
return db.get_item(item_id)from fastapi import Path, HTTPException, status
import logging
logger = logging.getLogger(__name__)
@app.get("/users/{user_id}/data")
async def get_user_data(
user_id: int = Path(
...,
gt=0,
description="Must be positive integer"
)
):
'''
Best practices:
1. Validate type at declaration (int = Path(...))
2. Use constraints (gt=0)
3. Provide clear description
4. Check authorization/access
5. Log access for audit
'''
# Authorization check
current_user = get_current_user()
if current_user.id != user_id and not current_user.is_admin:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Access denied"
)
logger.info(f"User {current_user.id} accessed user {user_id} data")
user_data = db.get_user_data(user_id)
if not user_data:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
return user_dataResources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward