0
0
FastAPIframework~15 mins

Including routers in main app in FastAPI - Deep Dive

Choose your learning style9 modes available
Overview - Including routers in main app
What is it?
Including routers in the main FastAPI app means organizing your web application by splitting routes into separate groups called routers. Each router handles a set of related paths and logic. Then, you connect these routers to the main app so it knows about all the routes. This helps keep your code clean and easier to manage as your app grows.
Why it matters
Without routers, all routes would be in one big file, making it hard to find, fix, or add features. Routers let you work on parts of your app independently, like teams working on different rooms in a house. This saves time, reduces mistakes, and makes your app easier to understand and maintain.
Where it fits
Before learning routers, you should know how to create a basic FastAPI app and define simple routes. After mastering routers, you can learn about middleware, dependency injection, and advanced routing features like path parameters and security.
Mental Model
Core Idea
Routers are like separate mini-apps that group related routes, which you then plug into the main FastAPI app to build a clean, organized web application.
Think of it like...
Imagine building a shopping mall where each store is a router. Each store manages its own products and customers, but all stores connect to the mall's main entrance, which is the main app.
Main App
  ├── Router A (e.g., users)
  │     ├── /users/
  │     └── /users/{id}
  ├── Router B (e.g., items)
  │     ├── /items/
  │     └── /items/{id}
  └── Router C (e.g., orders)
        ├── /orders/
        └── /orders/{id}
Build-Up - 7 Steps
1
FoundationCreate a basic FastAPI app
🤔
Concept: Learn how to start a simple FastAPI application and define a single route.
from fastapi import FastAPI app = FastAPI() @app.get('/') async def read_root(): return {"message": "Hello, world!"}
Result
When you run this app and visit '/', you see a JSON message: {"message": "Hello, world!"}.
Understanding how to create a FastAPI app and define routes is the foundation for organizing routes later with routers.
2
FoundationDefine routes in a separate router
🤔
Concept: Learn how to create a router to group related routes outside the main app.
from fastapi import APIRouter router = APIRouter() @router.get('/hello') async def say_hello(): return {"message": "Hello from router!"}
Result
This router defines a route '/hello' that returns a greeting message when called.
Separating routes into routers helps keep code modular and focused on specific features.
3
IntermediateInclude router in main app
🤔Before reading on: Do you think including a router requires copying its routes into the main app or just linking it? Commit to your answer.
Concept: Learn how to connect a router to the main FastAPI app so its routes become active.
from fastapi import FastAPI from myrouter import router # assume router defined as above app = FastAPI() app.include_router(router)
Result
The main app now responds to '/hello' because the router's routes are included.
Including routers links their routes to the main app without copying code, enabling modular design.
4
IntermediateUse prefixes and tags with routers
🤔Before reading on: Do you think adding a prefix changes the router's internal route paths or just how they appear externally? Commit to your answer.
Concept: Learn to add a prefix to all routes in a router and use tags for documentation grouping.
from fastapi import APIRouter router = APIRouter(prefix='/users', tags=['users']) @router.get('/') async def list_users(): return ["Alice", "Bob"]
Result
The route is now accessible at '/users/' and appears under 'users' in API docs.
Prefixes help organize routes under a common path, making URLs clearer and API docs easier to navigate.
5
IntermediateInclude multiple routers in main app
🤔
Concept: Learn how to add several routers to the main app to handle different parts of the application.
from fastapi import APIRouter, FastAPI users_router = APIRouter(prefix='/users') items_router = APIRouter(prefix='/items') @users_router.get('/') async def get_users(): return ["Alice", "Bob"] @items_router.get('/') async def get_items(): return ["Item1", "Item2"] app = FastAPI() app.include_router(users_router) app.include_router(items_router)
Result
The app responds to '/users/' and '/items/' with their respective data.
Using multiple routers scales your app cleanly by separating concerns into logical groups.
6
AdvancedRouter dependencies and shared state
🤔Before reading on: Do you think dependencies added to a router apply only to its routes or to the whole app? Commit to your answer.
Concept: Learn how to add dependencies to routers so all their routes share common logic like authentication.
from fastapi import Depends, APIRouter def verify_token(): # pretend token check return True router = APIRouter(dependencies=[Depends(verify_token)]) @router.get('/secure') async def secure_route(): return {"message": "Secure content"} app = FastAPI() app.include_router(router)
Result
All routes in this router require the verify_token dependency before running.
Router-level dependencies let you apply shared checks or setup to groups of routes efficiently.
7
ExpertRouter include order and route conflicts
🤔Before reading on: If two routers have the same route path, does the order of include_router calls affect which route is used? Commit to your answer.
Concept: Understand how FastAPI resolves route conflicts when multiple routers have overlapping paths and how include order matters.
router1 = APIRouter() @router1.get('/conflict') async def route1(): return {"router": "one"} router2 = APIRouter() @router2.get('/conflict') async def route2(): return {"router": "two"} app = FastAPI() app.include_router(router1) app.include_router(router2)
Result
The '/conflict' route responds with router2's handler because it was included last, overriding router1's route.
Knowing that later included routers override earlier ones prevents unexpected route shadowing bugs in complex apps.
Under the Hood
FastAPI uses Starlette's routing system. When you include a router, FastAPI adds its routes to the main app's internal route list. Each route has a path and handler function. When a request comes in, FastAPI checks this list in order to find the first matching route. Routers are just collections of routes bundled together, so including them merges their routes into the main app's routing table.
Why designed this way?
This design allows modular development by letting developers build and test parts of the app independently. It also keeps the main app lightweight and flexible. Alternatives like putting all routes in one file become unmanageable as apps grow. The router system balances simplicity with scalability.
Main App Routing Table
╔════════════════════════════════╗
║ /users/          -> users_handler ║
║ /users/{id}      -> user_detail_handler ║
║ /items/          -> items_handler ║
║ /items/{id}      -> item_detail_handler ║
╚════════════════════════════════╝

Including a router adds its routes here, merging all routes for request matching.
Myth Busters - 4 Common Misconceptions
Quick: Does including a router copy its routes into the main app or just link them? Commit to your answer.
Common Belief:Including a router copies its routes into the main app as separate copies.
Tap to reveal reality
Reality:Including a router links its routes to the main app's routing table without copying, so changes to the router affect the app directly.
Why it matters:Thinking routes are copied can lead to confusion when changes to routers don't appear in the app or cause unexpected behavior.
Quick: If two routers have the same route path, does FastAPI merge them or override? Commit to your answer.
Common Belief:FastAPI merges routes with the same path from different routers without conflict.
Tap to reveal reality
Reality:FastAPI uses the last included router's route for conflicting paths, overriding earlier ones.
Why it matters:Ignoring this can cause silent route shadowing, where some routes become unreachable, leading to bugs.
Quick: Does adding a prefix to a router change the internal route definitions? Commit to your answer.
Common Belief:Adding a prefix modifies the router's internal route paths permanently.
Tap to reveal reality
Reality:Prefixes only affect how routes appear externally when included; internal route definitions remain unchanged.
Why it matters:Misunderstanding this can cause confusion when debugging route paths or reusing routers with different prefixes.
Quick: Do router-level dependencies apply globally to the app? Commit to your answer.
Common Belief:Dependencies added to a router apply to the entire FastAPI app.
Tap to reveal reality
Reality:Router-level dependencies apply only to routes within that router, not the whole app.
Why it matters:Assuming global effect can cause security holes or redundant checks if dependencies are misapplied.
Expert Zone
1
Router prefixes can be nested by including routers inside other routers, allowing deep route hierarchies.
2
Route order matters: FastAPI matches routes in the order they were added, so careful include order prevents shadowing.
3
Router dependencies can be combined with route-level dependencies, and understanding their interaction is key for complex authorization.
When NOT to use
Routers are less useful for very small apps with only a few routes where adding them adds unnecessary complexity. For such cases, defining routes directly in the main app is simpler. Also, if you need dynamic route generation at runtime, routers may be less flexible than custom routing logic.
Production Patterns
In production, routers are used to separate API versions (e.g., /v1, /v2), group features (users, items, orders), and apply middleware or dependencies per group. Teams often work on different routers independently, enabling parallel development and easier testing.
Connections
Modular programming
Routers implement modular design principles in web apps.
Understanding routers as modules helps grasp how software is broken into manageable, reusable parts.
Microservices architecture
Routers group related routes like microservices group related services.
Knowing routers prepares you for designing microservices by thinking in terms of bounded contexts and clear interfaces.
Electrical circuit breakers
Routers act like circuit breakers that control flow in different parts of a system.
This cross-domain view shows how controlling flow and grouping parts is a universal design pattern.
Common Pitfalls
#1Including routers without prefixes causes route path clashes.
Wrong approach:app.include_router(users_router) app.include_router(items_router) # both have '/' route without prefix
Correct approach:app.include_router(users_router, prefix='/users') app.include_router(items_router, prefix='/items')
Root cause:Not using prefixes leads to overlapping routes that FastAPI cannot distinguish.
#2Defining routes inside routers but forgetting to include the router in the main app.
Wrong approach:router = APIRouter() @router.get('/hello') async def hello(): return "Hi" app = FastAPI() # router not included
Correct approach:app = FastAPI() app.include_router(router)
Root cause:Forgetting to include routers means their routes never become active in the app.
#3Adding dependencies to the main app expecting them to apply to router routes.
Wrong approach:app = FastAPI(dependencies=[Depends(auth)]) app.include_router(router) # router routes may not inherit dependencies as expected
Correct approach:router = APIRouter(dependencies=[Depends(auth)]) app.include_router(router)
Root cause:Dependencies must be added at the correct level to affect intended routes.
Key Takeaways
Including routers in FastAPI helps organize routes into logical groups, making apps easier to build and maintain.
Routers are connected to the main app by include_router, which merges their routes into the app's routing table.
Using prefixes and tags with routers improves URL structure and API documentation clarity.
The order of including routers matters because later routers can override routes from earlier ones.
Router-level dependencies apply only to that router's routes, enabling shared logic without affecting the whole app.