Skip to content

Chapter 19 · Large Project Structure with APIRouter ​

Chapter goals: Learn to use APIRouter to 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.

.
├── 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.py

Key points:

  • Every directory needs __init__.py to make it a Python package for cross-imports;
  • app/main.py is 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:

python
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:

python
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:

python
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 ​

  • APIRouter is a "mini FastAPI" for modular route organization;
  • Recommended structure: app/routers/ for route modules, app/dependencies.py for shared deps, app/main.py for assembly;
  • prefix adds to all paths in the router (do not end with /);
  • Router-level dependencies apply 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 ​

  1. 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.
  2. Add a router-level dependencies=[Depends(get_token_header)] to the items router; verify all /items/* routes require the header.
  3. 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.