FastAPI APIRouter
As your FastAPI application grows, keeping all your routes in a single file becomes unwieldy and difficult to maintain. This is where APIRouter
comes in - it's one of FastAPI's most powerful features that helps you organize your application into modular components.
What is APIRouter?
APIRouter
is a class in FastAPI that allows you to create modular, reusable route handlers. Think of it as a "mini FastAPI application" that you can include in your main application. By using APIRouter
, you can:
- Organize your routes into separate files and directories
- Group related endpoints together
- Apply common configurations to a group of routes
- Make your code more maintainable and easier to test
Basic Usage of APIRouter
Let's start with a simple example to see how APIRouter
works:
from fastapi import APIRouter, FastAPI
app = FastAPI()
# Create a router instance
router = APIRouter()
# Define routes on the router
@router.get("/items/")
async def read_items():
return [{"name": "Item 1"}, {"name": "Item 2"}]
@router.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
# Include the router in the main app
app.include_router(router)
Running this application and navigating to /items/
will return:
[
{
"name": "Item 1"
},
{
"name": "Item 2"
}
]
And navigating to /items/42
will return:
{
"item_id": 42
}
Organizing Routes with Prefixes
One of the key benefits of APIRouter
is the ability to add a prefix to all routes in the router. This is especially useful when organizing your API by resource or feature:
from fastapi import APIRouter, FastAPI
app = FastAPI()
# Create a router with a prefix
router = APIRouter(prefix="/items")
# Now paths are relative to the prefix
@router.get("/") # This becomes /items/
async def read_items():
return [{"name": "Item 1"}, {"name": "Item 2"}]
@router.get("/{item_id}") # This becomes /items/{item_id}
async def read_item(item_id: int):
return {"item_id": item_id}
# Include the router in the main app
app.include_router(router)
Organizing Routes in Separate Files
To fully benefit from APIRouter
, you should organize your routes in separate files. Here's a typical project structure:
my_app/
├── main.py
├── routers/
│ ├── __init__.py
│ ├── items.py
│ ├── users.py
│ └── orders.py
Let's see how this works:
routers/items.py:
from fastapi import APIRouter
router = APIRouter(
prefix="/items",
tags=["items"],
responses={404: {"description": "Not found"}},
)
@router.get("/")
async def read_items():
return [{"name": "Item 1"}, {"name": "Item 2"}]
@router.get("/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
routers/users.py:
from fastapi import APIRouter
router = APIRouter(
prefix="/users",
tags=["users"],
responses={404: {"description": "Not found"}},
)
@router.get("/")
async def read_users():
return [{"name": "John Doe"}, {"name": "Jane Smith"}]
@router.get("/{user_id}")
async def read_user(user_id: int):
return {"user_id": user_id}
main.py:
from fastapi import FastAPI
from routers import items, users
app = FastAPI(title="My API")
app.include_router(items.router)
app.include_router(users.router)
@app.get("/")
async def root():
return {"message": "Welcome to my API!"}
Advanced APIRouter Configuration
APIRouter
provides several optional parameters to customize its behavior:
prefix
: A string that will be prepended to all route pathstags
: A list of strings to be applied to all routes for API documentation groupingdependencies
: Dependencies that should be included in all routesresponses
: Default responses for all routescallbacks
: Default callbacks for all routes
Here's how to use some of these advanced features:
from fastapi import APIRouter, Depends, HTTPException, Security
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def get_token_header(token: str = Security(oauth2_scheme)):
if token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="Invalid token")
return token
router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)], # Applied to all routes
responses={404: {"description": "Not found"}},
)
@router.get("/")
async def read_items():
# This endpoint requires a valid token due to the router dependencies
return [{"name": "Item 1"}, {"name": "Item 2"}]
Nested Routers
You can even include routers within other routers to create deeply nested route structures:
from fastapi import APIRouter, FastAPI
app = FastAPI()
parent_router = APIRouter(prefix="/parent")
child_router = APIRouter(prefix="/child")
@child_router.get("/items")
async def read_child_items():
return [{"name": "Child Item 1"}, {"name": "Child Item 2"}]
# Include child router in parent router
parent_router.include_router(child_router)
# Include parent router in app
app.include_router(parent_router)
# This creates a path: /parent/child/items
Real-World Example: Building a Blog API
Let's create a more comprehensive example showing how APIRouter
can be used in a real-world scenario. We'll build a simplified blog API with routes for posts, comments, and users.
routers/posts.py:
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List, Optional
router = APIRouter(prefix="/posts", tags=["posts"])
class Post(BaseModel):
id: Optional[int] = None
title: str
content: str
author_id: int
# Dummy data
posts_db = [
Post(id=1, title="First Post", content="Hello world!", author_id=1),
Post(id=2, title="FastAPI Rocks", content="FastAPI is amazing!", author_id=1)
]
@router.get("/", response_model=List[Post])
async def get_posts():
return posts_db
@router.get("/{post_id}", response_model=Post)
async def get_post(post_id: int):
for post in posts_db:
if post.id == post_id:
return post
raise HTTPException(status_code=404, detail="Post not found")
@router.post("/", response_model=Post, status_code=201)
async def create_post(post: Post):
post.id = max(p.id for p in posts_db) + 1 if posts_db else 1
posts_db.append(post)
return post
routers/users.py:
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List, Optional
router = APIRouter(prefix="/users", tags=["users"])
class User(BaseModel):
id: Optional[int] = None
username: str
email: str
# Dummy data
users_db = [
User(id=1, username="johndoe", email="[email protected]"),
User(id=2, username="janedoe", email="[email protected]")
]
@router.get("/", response_model=List[User])
async def get_users():
return users_db
@router.get("/{user_id}", response_model=User)
async def get_user(user_id: int):
for user in users_db:
if user.id == user_id:
return user
raise HTTPException(status_code=404, detail="User not found")
main.py:
from fastapi import FastAPI
from routers import posts, users
app = FastAPI(
title="Blog API",
description="A simple blog API built with FastAPI",
version="0.1.0"
)
app.include_router(posts.router)
app.include_router(users.router)
@app.get("/")
async def root():
return {
"message": "Welcome to the Blog API",
"documentation": "/docs"
}
APIRouter with Dependency Injection
You can also use APIRouter
with FastAPI's powerful dependency injection system to share functionality across routes:
from fastapi import APIRouter, Depends, HTTPException
from typing import Optional
def common_parameters(q: Optional[str] = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}
router = APIRouter(prefix="/items", tags=["items"])
@router.get("/")
async def read_items(commons: dict = Depends(common_parameters)):
# You can access commons["q"], commons["skip"], commons["limit"]
return {
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
"query_params": commons
}
@router.get("/search")
async def search_items(commons: dict = Depends(common_parameters)):
# Same dependency used in multiple routes
return {
"items": [{"item_id": "Foo"}, {"item_id": "Bar"}],
"query_params": commons
}
Summary
APIRouter
is a powerful tool in FastAPI that helps you:
- Organize your code into modular, maintainable components
- Group related endpoints with common prefixes, tags, and dependencies
- Split your API logic into separate files for better code organization
- Apply common configurations to groups of routes
- Create nested route structures for complex APIs
By leveraging APIRouter
effectively, you can build FastAPI applications that scale well as they grow in complexity, keeping your codebase organized and maintainable.
Additional Resources
Exercises
- Create a simple library management API with routers for books, authors, and borrowers.
- Build an e-commerce API with routers for products, orders, and customers with authentication requirements.
- Extend the blog API example with a comments router that includes routes for creating, reading, updating, and deleting comments.
- Create a nested router structure for a social media API with routes for users, posts, comments, and likes.
- Implement dependency injection for authentication across different routers in any of the above examples.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)