Backend
Mastering Routing in FastAPI: From Flat Files to Clean Architecture
Arun Yadav Dev.to (EN Zone)
2 views
When building an API with FastAPI, it’s deceptively easy to dump every endpoint into main.py. A few @app.get() decorators here, a couple of @app.post() decorators there, and everything works.
Then your project grows. Suddenly, main.py is an 800-line monolith, team members are constantly hitting Git merge conflicts, and nobody knows where the user authentication logic ends and the billing logic begins.
FastAPI solves this with APIRouter. Here is how to structure your routing cleanly from day one.
1. The Core Tool: APIRouter
Think of APIRouter as a mini FastAPI app that doesn’t run on its own. It groups related path operations together, which you then mount onto your main application.
A typical modular directory structure looks like this:
my_project/
├── app/
│ ├── routers/
│ │ ├── users.py
│ │ └── items.py
│ ├── dependencies.py
│ └── main.py
└── requirements.txt
In your module (app/routers/users.py), define your router:
from fastapi import APIRouter, HTTPException
router = APIRouter()
@router.get("/")
async def list_users():
return [{"id": 1, "username": "alice"}]
@router.get("/{user_id}")
async def get_user(user_id: int):
return {"id": user_id, "username": "alice"}
Then, register it in app/main.py:
from fastapi import FastAPI
from app.routers import users
app = FastAPI()
app.include_router(users.router, prefix="/users", tags=["Users"])
Two key parameters to notice:
prefix: Prepend /users to every endpoint in the router. Notice how users.py uses / and /{user_id} instead of repeating /users everywhere.
tags: Groups these endpoints together in FastAPI's auto-generated Swagger UI (/docs).
2. Cleaner Endpoints with Shared Dependencies
One of the biggest advantages of APIRouter is attaching security checks or common dependencies to an entire group of routes at once.
Instead of writing Depends(verify_token) on 15 separate endpoints, apply it to the router level:
# app/routers/admin.py
from fastapi import APIRouter, Depends
from app.dependencies import require_admin_role
# Every route attached to this router will now require admin access
router = APIRouter(
prefix="/admin",
tags=["Admin"],
dependencies=[Depends(require_admin_role)]
)
@router.delete("/purge-cache")
async def purge_cache():
return {"status": "cache cleared"}
3. Nesting Routers for Sub-Resources
Real-world APIs often have hierarchical relationships—such as comments belonging to a specific post (/posts/{post_id}/comments).
Rather than hardcoding long prefix strings, routers can be nested inside other routers:
# app/routers/comments.py
from fastapi import APIRouter
comments_router = APIRouter()
@comments_router.get("/")
async def get_comments(post_id: int):
return {"post_id": post_id, "comments": []}
# app/routers/posts.py
from fastapi import APIRouter
from app.routers.comments import comments_router
posts_router = APIRouter(prefix="/posts", tags=["Posts"])
# Mount comments directly under a specific post
posts_router.include_router(
comments_router,
prefix="/{post_id}/comments",
tags=["Comments"]
)
Now, mounting posts_router inside your main.py automatically exposes both /posts and /posts/{post_id}/comments cleanly.
4. API Versioning Without Headaches
Versioning is simple when your routing is modular. You can bundle whole sets of routers under a version prefix:
# app/main.py
from fastapi import FastAPI, APIRouter
from app.routers import users, items
app = FastAPI()
# Create a v1 router
api_v1 = APIRouter(prefix="/api/v1")
api_v1.include_router(users.router, prefix="/users", tags=["Users"])
api_v1.include_router(items.router, prefix="/items", tags=["Items"])
# Include the full v1 bundle into the main app
app.include_router(api_v1)
When it's time for v2, you can spin up an api_v2 router alongside it without touching older client integrations.
Read original: https://dev.to/itsdevcode/mastering-routing-in-fastapi-from-flat-files-to-clean-architecture-427g
← Previous
4 pitfalls of loop engineering (and how to fix them)
Next →
Your n8n Workflow Has 40 Nodes. Should Any of Them Be an AI Agent?
Related
Building a Polymarket TWAP State Machine for Trading Bots
Backend
0
Dev.to (EN Zone)
Networking Needs a Protocol: Build a Consent-First Community Introduction Flow
Backend
2
DEV Community
Letting an AI agent follow a relation without widening what it can see
Backend
2
DEV Community
Why Rider and ReSharper Were Slow to Start, and How Microsoft Helped Fix the Problem
Backend
1
JetBrains Blog
Comments0
No comments yet — be the first