Chapter 19 · Large Project Structure with APIRouter
Chapter goals: Learn to use
APIRouterto split a single-file app into a maintainable multi-module project, understand the officially recommended directory structure, and master dependency sharing across routers.
19.1 Why APIRouter Exists
You cannot put all path operations in one main.py for a real project. FastAPI's answer is APIRouter — think of it as a "mini FastAPI": it accepts the same parameters as the FastAPI class (dependencies, tags, responses, etc.), declare routes in their own modules, then "plug back" into the main app at the end. If you have used Flask, it is equivalent to Blueprint.
19.2 Officially Recommended Project Structure
.
├── app
│ ├── __init__.py # Makes app a Python package
│ ├── main.py # Main module: assembles all routers
│ ├── dependencies.py # Dependencies shared across routers
│ ├── routers # Router sub-package
│ │ ├── __init__.py
│ │ ├── items.py # /items related path operations
│ │ └── users.py # /users related path operations
│ └── internal # Internal sub-package (e.g., admin interfaces)
│ ├── __init__.py
│ └── admin.pyKey points:
- Every directory needs
__init__.pyto make it a Python package for cross-imports; app/main.pyis the assembly point — after dispersing business logic into modules, it stays very thin;- Shared dependencies go in
app/dependencies.py, not scattered across router files.
19.3 Writing a Router Module
app/routers/users.py — routes concerned only with users:
from fastapi import APIRouter, Depends, HTTPException
# Single dot: resolve from current package (app/routers/) — dependencies.py does not exist here
# Double dot: go back to parent package app/ to find app/dependencies.py ✅
from ..dependencies import get_token_header
# prefix adds to all paths in this router; tags groups them in docs
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/")
async def read_users():
return [{"username": "alice"}, {"username": "bob"}]
@router.get("/me")
async def read_current_user(token: str = Depends(get_token_header)):
return {"token": token}app/routers/items.py — demonstrating router-level common config:
from fastapi import APIRouter, Depends, HTTPException
from ..dependencies import get_token_header, get_query_token
router = APIRouter(
prefix="/items", # Note: must NOT end with /
tags=["items"],
dependencies=[Depends(get_token_header)], # All routes in this group require X-Token
responses={404: {"description": "Not found"}}, # Pre-defined response goes into docs
)
@router.get("/") # Actual path: /items/
async def read_items():
return [{"item_id": "Foo"}]
@router.get("/{item_id}") # Actual path: /items/{item_id}
async def read_item(item_id: str):
return {"item_id": item_id}prefix detail
Every path operation's path must start with /, so prefix must NOT end with / (write /items, not /items/). Router-level dependencies apply to the entire group and run before route-level dependencies — a typical use is batch-adding auth to a whole group of endpoints.
19.4 Assembling in the Main App
app/main.py is responsible only for assembly:
from fastapi import Depends, FastAPI
from .routers import items, users
from .dependencies import get_query_token
# Global dependencies stack with each router's dependencies
app = FastAPI(dependencies=[Depends(get_query_token)])
app.include_router(items.router)
app.include_router(users.router)19.5 Chapter Summary
APIRouteris a "mini FastAPI" for modular route organization;- Recommended structure:
app/routers/for route modules,app/dependencies.pyfor shared deps,app/main.pyfor assembly; prefixadds to all paths in the router (do not end with/);- Router-level
dependenciesapply to the entire group; include_router()assembles all routers in the main app.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What is APIRouter equivalent to in Flask?
2. Why must prefix NOT end with a trailing slash?
3. What is the purpose of the double-dot import from ..dependencies?
4. What does router-level dependencies=[Depends(auth)] do?
🛠️ Hands-On Exercises
- Create the official project structure:
app/main.py,app/routers/items.py,app/routers/users.py,app/dependencies.py; assemble all routers in main.py. - Add a router-level
dependencies=[Depends(get_token_header)]to the items router; verify all /items/* routes require the header. - Refactor a monolithic single-file FastAPI app into the multi-router structure; verify all routes still work identically.
Proceed to Chapter 20: Testing FastAPI Applications.