Building Modular APIs with FastAPI's APIRouter
FastAPI's APIRouter facilitates the decomposision of endpoint collections into discrete, self-contained modules, a critical pattern for scaling applications beyond monolithic script structures.
Instantiate APIRouter to encapsulate related operations. Below illustrates a customer management submodule:
from fastapi import APIRouter
customer_api = APIRouter()
@customer_api.get("/clients/", tags=["clients"])
async def list_clients():
return [{"id": "C001", "name": "TechCorp"}, {"id": "C002", "name": "DataSystems"}]
@customer_api.get("/clients/active")
async def current_client_session():
return {"id": "CURRENT", "permissions": "read-write"}
@customer_api.get("/clients/{client_id}")
async def fetch_client_details(client_id: str):
return {"client_id": client_id, "metadata": {}}
Constructor parameters enable consistent route prefixing and cross-cutting concerns. This inventory module demonstrates authentication requirements and standardized error responses:
from fastapi import Header, HTTPException, Depends, APIRouter
async def validate_bearer_token(authorization: str = Header()):
if not authorization.startswith("Bearer valid_"):
raise HTTPException(status_code=401, detail="Authentication failed")
async def mandatory_query_token(token: str):
if token != "live_session":
raise HTTPException(status_code=400, detail="Session token invalid")
stock_api = APIRouter(
prefix="/inventory",
tags=["stock"],
dependencies=[Depends(validate_bearer_token)],
responses={404: {"description": "SKU does not exist"}}
)
@stock_api.get("/products/{product_code}")
async def get_product_info(product_code: str):
if product_code not in product_catalog:
raise HTTPException(status_code=404, detail="Product unavailable")
return {"sku": product_code, "details": product_catalog[product_code]}
The prefix parameter mounts all contained routes under /inventory, while the dependencies list executes validate_bearer_token prior to every endpoint handler within this router.
Integrate modular components into the primary application via include_router:
from fastapi import FastAPI, Depends
from .security import mandatory_query_token, validate_bearer_token
from .modules import customer_api, stock_api
from .admin_panel import admin_views
app = FastAPI(dependencies=[Depends(mandatory_query_token)])
app.include_router(customer_api)
app.include_router(stock_api)
app.include_router(
admin_views,
prefix="/admin",
tags=["system-admin"],
dependencies=[Depends(validate_bearer_token)],
responses={503: {"description": "Maintenance mode"}}
)
@app.get("/status")
async def api_status():
return {"environment": "production", "version": "2.1.0"}
Dependencies declared at the FastAPI class level propagate throughout all registered routers, creating a layered security model where global validators execute first, followed by router-specific middleware.