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.
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:
{
"message": "Hello World"
}
This is a basic path operation that:
- Uses the
@app.get("/")
decorator to indicate it handles GET requests on the root path (/
) - Defines a Python function that returns a dictionary (which FastAPI automatically converts to JSON)
HTTP Methods
FastAPI supports all standard HTTP methods:
@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.
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:
{
"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:
@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:
{
"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:
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.
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:
[
{"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:
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:
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:
{
"name": "Foo",
"price": 45.2,
"description": "Optional description"
}
You'll get a response like:
{
"name": "Foo",
"description": "Optional description",
"price": 45.2,
"tax": null
}
Multiple Parameters
You can combine path parameters, query parameters, and request body:
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:
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:
- Define routes with specific HTTP methods
- Extract data from URLs using path parameters
- Handle optional data with query parameters
- Process request bodies using Pydantic models
- Combine different parameter types in a single endpoint
- Return structured responses
Understanding path operations is essential for building RESTful APIs with FastAPI.
Additional Resources
- FastAPI Official Documentation on Path Parameters
- FastAPI Query Parameters Documentation
- Advanced Request Body Usage in FastAPI
Exercises
-
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
-
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
-
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! :)