Skip to main content

FastAPI Path Parameters

Path parameters are a fundamental concept in web API development that allow you to create dynamic routes. They capture values from specific parts of the URL path and make them available to your API functions. In this tutorial, we'll explore how to use path parameters in FastAPI to build flexible and intuitive APIs.

What are Path Parameters?

Path parameters (also called URL parameters) are variable parts of a URL path that change based on the resource being accessed. For example, in the URL /users/123, "123" could be a path parameter that represents a user ID.

Path parameters let you:

  • Create dynamic routes that respond differently based on the values in the URL
  • Design RESTful APIs that follow resource-based URL conventions
  • Capture important identifiers directly from the URL path

Defining Path Parameters in FastAPI

In FastAPI, path parameters are defined by placing variable names inside curly braces {} in your path string. The parameter values will automatically be passed to your route function as arguments.

Let's create a basic example:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
def read_item(item_id):
return {"item_id": item_id}

In this example, when a user visits /items/42, FastAPI will:

  1. Match the route pattern /items/{item_id}
  2. Extract "42" as the value for item_id
  3. Call the read_item function with item_id="42"
  4. Return {"item_id": "42"} as a JSON response

Type Conversion and Validation

By default, all path parameters are treated as strings. However, FastAPI can automatically convert parameters to other types, which provides automatic validation. Let's modify our example:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id, "item_type": type(item_id).__name__}

Now if you access /items/42, FastAPI will:

  1. Convert "42" to the integer 42
  2. Validate that it can be converted to an integer
  3. Call read_item with item_id=42 (as an integer, not a string)
  4. Return {"item_id": 42, "item_type": "int"}

If someone accesses /items/foo, they'll get a validation error because "foo" can't be converted to an integer:

json
{
"detail": [
{
"loc": ["path", "item_id"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}

Supported Data Types

FastAPI supports many data types for path parameters, including:

  • int: Integer values
  • float: Floating-point numbers
  • str: Strings (the default)
  • bool: Boolean values
  • UUID: For UUID strings
  • Enum: For predefined sets of values

For example, here's how to use a UUID:

python
from fastapi import FastAPI
from uuid import UUID

app = FastAPI()

@app.get("/users/{user_id}")
def read_user(user_id: UUID):
return {"user_id": user_id}

This route would match URLs like /users/a86fc66d-4f6a-4b49-9aa7-1b2ba8e6640b.

Multiple Path Parameters

You can define multiple path parameters in a single route:

python
from fastapi import FastAPI

app = FastAPI()

@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}

This route handles URLs like /users/42/posts/123 and makes both IDs available to your function.

Predefined Values with Enum

If your path parameter should only accept specific values, you can use Python's Enum:

python
from enum import Enum
from fastapi import FastAPI

class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"

app = FastAPI()

@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
if model_name == ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}

if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}

return {"model_name": model_name, "message": "Have some residuals"}

This route will only match /models/alexnet, /models/resnet, or /models/lenet and will reject any other values.

Path Parameters with Paths

Sometimes you need to capture path parameters that contain path characters themselves (like /). FastAPI supports this with a special path converter:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/files/{file_path:path}")
def read_file(file_path: str):
return {"file_path": file_path}

The :path converter allows the parameter to match any path, including slashes. This route would match URLs like /files/images/logo.png and /files/documents/report.pdf.

Order Matters: Path Parameters vs. Fixed Paths

When defining routes with similar patterns, the order matters. For example:

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/users/me")
def read_current_user():
return {"user_id": "the current user"}

@app.get("/users/{user_id}")
def read_user(user_id: str):
return {"user_id": user_id}

If these routes were defined in reverse order, /users/me would never be matched because FastAPI would think "me" is a user_id parameter. Always define fixed routes before routes with path parameters.

Real-world Example: Building a Blog API

Let's put everything together in a more comprehensive example by building a simplified blog API:

python
from fastapi import FastAPI, HTTPException
from enum import Enum
from typing import List, Dict, Any
from pydantic import BaseModel
from uuid import UUID, uuid4
from datetime import datetime

app = FastAPI()

# Mock database
class PostStatus(str, Enum):
draft = "draft"
published = "published"
archived = "archived"

class Post(BaseModel):
id: UUID
title: str
content: str
status: PostStatus
created_at: datetime

# Mock database
posts_db: Dict[UUID, Post] = {}

# Create some sample posts
for i in range(3):
post_id = uuid4()
posts_db[post_id] = Post(
id=post_id,
title=f"Sample Post {i+1}",
content=f"This is sample content for post {i+1}",
status=PostStatus.published,
created_at=datetime.now()
)

# Get all posts
@app.get("/blog/posts")
def get_posts() -> List[Dict[str, Any]]:
return [post.dict() for post in posts_db.values()]

# Get a specific post
@app.get("/blog/posts/{post_id}")
def get_post(post_id: UUID):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Post not found")
return posts_db[post_id]

# Filter posts by status
@app.get("/blog/posts/status/{status}")
def get_posts_by_status(status: PostStatus):
filtered_posts = [
post.dict() for post in posts_db.values()
if post.status == status
]
return filtered_posts

# Get posts from a specific year and month
@app.get("/blog/archive/{year}/{month}")
def get_archive_posts(year: int, month: int):
if not (1 <= month <= 12):
raise HTTPException(status_code=400, detail="Month must be between 1 and 12")

filtered_posts = [
post.dict() for post in posts_db.values()
if post.created_at.year == year and post.created_at.month == month
]
return filtered_posts

This example demonstrates:

  1. Using UUIDs as path parameters for post identification
  2. Using Enum to restrict post status values
  3. Multiple path parameters for archive filtering
  4. Proper error handling with HTTP exceptions

Summary

Path parameters in FastAPI provide a powerful way to create dynamic, RESTful APIs. They allow you to:

  • Capture values directly from the URL path
  • Automatically convert and validate these values with type annotations
  • Use various data types including integers, UUIDs, and enums
  • Handle complex paths with multiple parameters

When using path parameters, remember:

  • Order matters when defining routes
  • Type annotations provide automatic validation
  • Path parameters can be combined with query parameters (covered in another tutorial)

Exercises

To solidify your understanding of path parameters, try these exercises:

  1. Create a route that accepts a username as a path parameter and returns a greeting
  2. Build a route that accepts two numbers as path parameters and returns their sum
  3. Implement a product catalog API with routes for categories and products using path parameters
  4. Create a file management API that uses the :path converter to access files in different directories

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)