Skip to main content

FastAPI Path Operations

Introduction

Path operations are one of the core concepts in FastAPI. They define how your API will respond to different HTTP requests. A path operation is a combination of:

  • A path (like /users or /items/{item_id})
  • An HTTP method (like GET, POST, PUT, DELETE)
  • A function that handles the request and returns a response

In this tutorial, you'll learn how to define path operations, handle parameters, and implement various HTTP methods to build robust APIs with FastAPI.

Basic Path Operations

Let's start with a simple path operation that responds to a GET request.

python
from fastapi import FastAPI

app = FastAPI()

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

When you run this code and navigate to http://localhost:8000/, you'll see:

json
{
"message": "Hello World"
}

This is a basic path operation that:

  1. Uses the @app.get("/") decorator to indicate it handles GET requests on the root path (/)
  2. Defines a Python function that returns a dictionary (which FastAPI automatically converts to JSON)

HTTP Methods

FastAPI supports all standard HTTP methods:

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

@app.post("/items")
def create_item(item: dict):
return {"item": item, "created": True}

@app.put("/items/{item_id}")
def update_item(item_id: int, item: dict):
return {"item_id": item_id, "item": item, "updated": True}

@app.delete("/items/{item_id}")
def delete_item(item_id: int):
return {"item_id": item_id, "deleted": True}

Each decorator corresponds to an HTTP method:

  • @app.get(): Read data
  • @app.post(): Create data
  • @app.put(): Update data
  • @app.delete(): Delete data
  • @app.patch(): Partially update data
  • @app.options(): Preflight requests
  • @app.head(): Same as GET but without response body

Path Parameters

Path parameters are variable parts of a URL path. They allow you to capture values from the URL.

python
from fastapi import FastAPI

app = FastAPI()

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

If you navigate to http://localhost:8000/users/123, you'll see:

json
{
"user_id": 123
}

Note that FastAPI automatically converts user_id to an integer because we declared it as user_id: int.

Type Conversion and Validation

FastAPI provides automatic type validation for path parameters:

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

If someone tries to access /items/abc (not an integer), FastAPI will return a clear error message:

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

Predefined Path Parameters

You can use Enum for predefined values:

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

Query Parameters

Query parameters are the key-value pairs that appear after ? in a URL.

python
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
fake_items = [{"id": i, "name": f"Item {i}"} for i in range(100)]
return fake_items[skip : skip + limit]

If you navigate to http://localhost:8000/items/?skip=20&limit=5, you'll get items 20-24:

json
[
{"id": 20, "name": "Item 20"},
{"id": 21, "name": "Item 21"},
{"id": 22, "name": "Item 22"},
{"id": 23, "name": "Item 23"},
{"id": 24, "name": "Item 24"}
]

Optional Query Parameters

By defining a parameter with a default value (None or otherwise), you make it optional:

python
from typing import Optional
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
def read_item(item_id: int, q: Optional[str] = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}

Request Body

To receive data in the request body, declare it using Pydantic models:

python
from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None

app = FastAPI()

@app.post("/items/")
def create_item(item: Item):
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict

To test this endpoint, send a POST request with a JSON body:

json
{
"name": "Foo",
"price": 45.2,
"description": "Optional description"
}

You'll get a response like:

json
{
"name": "Foo",
"description": "Optional description",
"price": 45.2,
"tax": null
}

Multiple Parameters

You can combine path parameters, query parameters, and request body:

python
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None

app = FastAPI()

@app.put("/items/{item_id}")
def update_item(
item_id: int,
item: Item,
q: Optional[str] = None
):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result

Real-world Example: Building a Blog API

Let's create a simple blog API to demonstrate path operations in a real-world scenario:

python
from typing import List, Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from datetime import datetime

app = FastAPI()

# Data models
class Post(BaseModel):
title: str
content: str
published: bool = True

class PostResponse(Post):
id: int
created_at: datetime

# In-memory database
posts_db = {}
post_counter = 0

# Path operations
@app.post("/posts/", response_model=PostResponse)
def create_post(post: Post):
global post_counter
post_counter += 1
post_dict = post.dict()
new_post = {**post_dict, "id": post_counter, "created_at": datetime.now()}
posts_db[post_counter] = new_post
return new_post

@app.get("/posts/", response_model=List[PostResponse])
def list_posts(skip: int = 0, limit: int = 10, published_only: bool = False):
results = list(posts_db.values())
if published_only:
results = [post for post in results if post["published"]]
return results[skip:skip+limit]

@app.get("/posts/{post_id}", response_model=PostResponse)
def get_post(post_id: int):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Post not found")
return posts_db[post_id]

@app.put("/posts/{post_id}", response_model=PostResponse)
def update_post(post_id: int, post: Post):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Post not found")
update_data = post.dict()
stored_post = posts_db[post_id]
stored_post.update(update_data)
return stored_post

@app.delete("/posts/{post_id}", response_model=dict)
def delete_post(post_id: int):
if post_id not in posts_db:
raise HTTPException(status_code=404, detail="Post not found")
del posts_db[post_id]
return {"message": "Post deleted successfully"}

This blog API demonstrates:

  • Different HTTP methods (GET, POST, PUT, DELETE)
  • Path parameters for specific resources
  • Query parameters for filtering and pagination
  • Pydantic models for data validation
  • Response models for consistent output
  • Error handling with HTTPException

Summary

Path operations are fundamental building blocks in FastAPI. They allow you to:

  1. Define routes with specific HTTP methods
  2. Extract data from URLs using path parameters
  3. Handle optional data with query parameters
  4. Process request bodies using Pydantic models
  5. Combine different parameter types in a single endpoint
  6. Return structured responses

Understanding path operations is essential for building RESTful APIs with FastAPI.

Additional Resources

Exercises

  1. Create a simple to-do list API with endpoints to:

    • List all to-do items
    • Get a specific to-do item
    • Create a new to-do item
    • Update an existing to-do item
    • Delete a to-do item
  2. Implement an API for a library system where you can:

    • List books with filtering by author and genre
    • Get book details
    • Add new books
    • Update book information
    • Mark books as borrowed/returned
  3. Extend the blog API example with features like:

    • Comments on posts
    • Categories for posts
    • Author information
    • Search functionality


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