Skip to main content

FastAPI Path Operations

Introduction

Path operations are the core building blocks of any FastAPI application. In web development terminology, a "path operation" refers to the combination of a path (URL) and an operation (HTTP method such as GET, POST, PUT, DELETE). In FastAPI, these are implemented as Python functions decorated with the appropriate HTTP method decorator.

This tutorial will teach you how to define routes in your FastAPI application, handle different HTTP methods, and use path parameters to create dynamic endpoints.

Basic Path Operations

Let's start with the simplest path operation - defining a route that responds to GET requests.

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
return {"message": "Hello World"}

When you run this application and access the root URL (/), FastAPI will return a JSON response with the message "Hello World".

What's happening here?

  1. We import FastAPI from the fastapi package
  2. We create an instance of the FastAPI class
  3. We define a function read_root() that returns a dictionary
  4. We decorate the function with @app.get("/") to tell FastAPI this function should handle GET requests to the root path

HTTP Methods

FastAPI provides decorators for all standard HTTP methods:

python
@app.get("/items")
def read_items():
return {"items": ["item1", "item2"]}

@app.post("/items")
def create_item():
return {"message": "Item created"}

@app.put("/items/{item_id}")
def update_item(item_id: int):
return {"message": f"Item {item_id} updated"}

@app.delete("/items/{item_id}")
def delete_item(item_id: int):
return {"message": f"Item {item_id} deleted"}

Each decorator corresponds to a specific HTTP method and defines how your API responds to different types of requests.

Path Parameters

Path parameters allow you to capture values from the URL path. They are defined using curly braces {} in the path string:

python
from fastapi import FastAPI

app = FastAPI()

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

When you access /users/123, FastAPI will:

  1. Match the URL to the path /users/{user_id}
  2. Extract the value 123 from the URL
  3. Convert it to an integer (as specified in the function parameter)
  4. Call the function with user_id=123

If you try to access /users/abc, FastAPI will return an error because "abc" can't be converted to an integer.

Multiple Path Parameters

You can define multiple path parameters in a single path:

python
@app.get("/users/{user_id}/posts/{post_id}")
def read_user_post(user_id: int, post_id: int):
return {"user_id": user_id, "post_id": post_id}

Accessing /users/123/posts/456 would return:

json
{
"user_id": 123,
"post_id": 456
}

Path Parameters with Type Validation

FastAPI uses Python type hints to validate path parameters:

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

FastAPI supports various types:

python
from uuid import UUID
from datetime import datetime, date

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

@app.get("/items_str/{item_id}")
def read_item_str(item_id: str):
return {"item_id": item_id, "type": "string"}

@app.get("/items_uuid/{item_id}")
def read_item_uuid(item_id: UUID):
return {"item_id": str(item_id), "type": "uuid"}

@app.get("/items_date/{item_date}")
def read_item_date(item_date: date):
return {"item_date": item_date, "type": "date"}

Path Parameter Order Matters

When you have multiple path operations, the order matters! FastAPI will match the first path operation that fits the requested URL. Consider this example:

python
@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 you accessed /users/me, which path operation would handle it?

Because /users/me is declared first, FastAPI will use that handler. If they were reversed, /users/{user_id} would match first and me would be treated as a user_id value.

Predefined Path Parameters

Sometimes you want to limit the possible values for a path parameter. You can use Enum for this:

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

With this setup, FastAPI will:

  1. Only accept "alexnet", "resnet", or "lenet" as valid values for model_name
  2. Document these as the only allowed values in the OpenAPI schema
  3. Provide autocomplete for these values in the interactive API documentation

Real-world Application: Building a Simple Blog API

Let's create a more practical example - a simple blog API that demonstrates the use of different path operations:

python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Dict, Optional
from uuid import uuid4

app = FastAPI()

# Data model
class Post(BaseModel):
id: str
title: str
content: str
published: bool = False

# In-memory database
posts_db: Dict[str, Post] = {}

# Create a new post
@app.post("/posts/", response_model=Post)
def create_post(post: Post):
post_id = str(uuid4())
new_post = Post(id=post_id, **post.dict())
posts_db[post_id] = new_post
return new_post

# Get all posts
@app.get("/posts/", response_model=List[Post])
def get_all_posts():
return list(posts_db.values())

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

# Update a post
@app.put("/posts/{post_id}", response_model=Post)
def update_post(post_id: str, updated_post: Post):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Post not found")
posts_db[post_id] = updated_post
return updated_post

# Delete a post
@app.delete("/posts/{post_id}")
def delete_post(post_id: str):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Post not found")
deleted_post = posts_db.pop(post_id)
return {"message": f"Post '{deleted_post.title}' deleted successfully"}

# Publish a post (partial update)
@app.patch("/posts/{post_id}/publish")
def publish_post(post_id: str):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Post not found")
posts_db[post_id].published = True
return {"message": f"Post '{posts_db[post_id].title}' has been published"}

This simple blog API demonstrates:

  1. Different HTTP methods (GET, POST, PUT, DELETE, PATCH) for CRUD operations
  2. Path parameters to identify specific resources (posts)
  3. Request body validation using Pydantic models
  4. Error handling with HTTP exceptions
  5. Response models for consistent API responses

Summary

In this tutorial, you've learned:

  • How to define basic path operations using FastAPI decorators
  • How to use different HTTP methods (GET, POST, PUT, DELETE, etc.)
  • How to define and validate path parameters
  • How to use Enum for predefined path parameter values
  • How path operation ordering affects matching
  • A practical application of these concepts in a simple blog API

Path operations form the foundation of your FastAPI application. Mastering them enables you to create well-structured, intuitive, and powerful APIs.

Additional Resources

  1. FastAPI Official Documentation on Path Parameters
  2. HTTP Methods Explained
  3. RESTful API Design Best Practices

Exercises

  1. Create a simple "todo list" API with endpoints for creating, listing, updating, and deleting tasks
  2. Build an API for a bookstore with path operations for authors and books
  3. Extend the blog API example to include categories and tags for posts
  4. Create an API with nested resources (e.g., /users/{user_id}/orders/{order_id})
  5. Implement a search endpoint that uses query parameters along with path parameters


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