> ## Documentation Index
> Fetch the complete documentation index at: https://fastapi2day.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Modular Routing with FastAPI APIRouter and Prefixes

> Split FastAPI routes into dedicated router files using APIRouter, add prefixes and tags, and register them in main.py for a scalable project layout.

As your application grows, keeping every route in a single `main.py` file quickly becomes unmaintainable. A file that starts at 50 lines can reach 500 lines by the time you've added employees, departments, authentication, and payroll endpoints. FastAPI's `APIRouter` class solves this by letting you define routes in separate, self-contained files and then plug them into your main application with a single line. This lesson shows you how to split, organise, and register routers cleanly.

***

## Modular Router Architecture

Instead of a monolithic `main.py`, you organise routes by domain. The main app acts as a hub that imports and attaches these sub-routers:

```mermaid theme={null}
graph TD
    MainApp["main.py (FastAPI App)"]

    MainApp -->|includes| EmpRouter["routers/employees.py (APIRouter)"]
    MainApp -->|includes| DeptRouter["routers/departments.py (APIRouter)"]

    EmpRouter --> EmpRoutes["GET /employees\nPOST /employees\nDELETE /employees/{id}"]
    DeptRouter --> DeptRoutes["GET /departments\nPOST /departments"]
```

***

## Creating a Sub-Router

<Steps>
  <Step title="Create the routers folder">
    ```bash theme={null}
    mkdir routers
    touch routers/__init__.py
    ```
  </Step>

  <Step title="Create the employee router file">
    Create `routers/employees.py`:

    ```python theme={null}
    # routers/employees.py
    from fastapi import APIRouter, HTTPException, status
    from pydantic import BaseModel

    # 1. Create the APIRouter instance with a prefix and tag
    router = APIRouter(
        prefix="/employees",
        tags=["Employees"]
    )

    class Employee(BaseModel):
        id: int
        name: str
        department: str

    # Sample mock data
    EMPLOYEES = [
        Employee(id=1, name="Alice", department="Engineering"),
        Employee(id=2, name="Bob", department="HR")
    ]

    # 2. Use @router instead of @app
    @router.get("/", response_model=list[Employee])
    def list_employees():
        return EMPLOYEES

    @router.get("/{emp_id}", response_model=Employee)
    def get_employee(emp_id: int):
        for emp in EMPLOYEES:
            if emp.id == emp_id:
                return emp
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Employee not found"
        )

    @router.post("/", response_model=Employee, status_code=status.HTTP_201_CREATED)
    def create_employee(employee: Employee):
        EMPLOYEES.append(employee)
        return employee
    ```
  </Step>

  <Step title="Register the router in main.py">
    ```python theme={null}
    # main.py
    from fastapi import FastAPI
    from routers import employees

    app = FastAPI(title="Employee Management System")

    # Register the sub-router
    app.include_router(employees.router)

    @app.get("/")
    def home():
        return {"message": "Welcome to the Modular EMS API!"}
    ```
  </Step>

  <Step title="Run the server">
    ```bash theme={null}
    uvicorn main:app --reload
    ```

    FastAPI maps all employee routes automatically:

    * `GET /employees/`
    * `GET /employees/{emp_id}`
    * `POST /employees/`
  </Step>
</Steps>

***

## Understanding APIRouter Parameters

| Parameter             | Description                                                                                  |
| --------------------- | -------------------------------------------------------------------------------------------- |
| `prefix="/employees"` | Prepends this path to every route in the file. `@router.get("/")` becomes `GET /employees/`. |
| `tags=["Employees"]`  | Groups these routes under an "Employees" section in Swagger UI and ReDoc.                    |
| `dependencies=[...]`  | Apply dependencies (e.g., authentication) to every route in the router at once.              |

<Note>
  The `prefix` means you write shorter routes in each file. `@router.get("/")` is the employee list, and `@router.get("/{emp_id}")` is the single employee — the `/employees` prefix is added automatically when you register the router.
</Note>

***

## Adding a Second Router

Create `routers/departments.py`:

```python theme={null}
# routers/departments.py
from fastapi import APIRouter
from pydantic import BaseModel

router = APIRouter(
    prefix="/departments",
    tags=["Departments"]
)

class Department(BaseModel):
    id: int
    name: str

DEPARTMENTS = [
    Department(id=1, name="Engineering"),
    Department(id=2, name="Product"),
]

@router.get("/", response_model=list[Department])
def list_departments():
    return DEPARTMENTS

@router.post("/", response_model=Department, status_code=201)
def create_department(department: Department):
    DEPARTMENTS.append(department)
    return department
```

Register it in `main.py`:

```python theme={null}
# main.py
from fastapi import FastAPI
from routers import employees, departments

app = FastAPI(title="Employee Management System")

app.include_router(employees.router)
app.include_router(departments.router)

@app.get("/")
def home():
    return {"message": "Welcome!"}
```

***

## Scalable Folder Layout

For a modular project, organise your folders like this:

```text theme={null}
my_project/
│
├── main.py                    # App entrypoint
├── routers/                   # APIRouter files
│   ├── __init__.py
│   ├── employees.py           # Employee route endpoints
│   ├── departments.py         # Department route endpoints
│   └── auth.py                # Authentication endpoints
├── models/                    # Pydantic schemas
│   ├── __init__.py
│   └── employee.py
└── requirements.txt
```

<Tip>
  Adding a new resource is now just two steps: create `routers/payroll.py` and add `app.include_router(payroll.router)` to `main.py`. Every other file stays unchanged.
</Tip>

***

## Router-Level Dependencies

Apply a dependency to every route in a router at once — for example, requiring authentication for all admin endpoints:

```python theme={null}
from fastapi import APIRouter, Depends
from auth import get_current_user  # your auth dependency

admin_router = APIRouter(
    prefix="/admin",
    tags=["Admin"],
    dependencies=[Depends(get_current_user)]  # applied to ALL routes in this router
)

@admin_router.get("/stats")
def get_system_stats():
    return {"active_employees": 42}

@admin_router.delete("/employees/{employee_id}")
def admin_delete_employee(employee_id: int):
    return {"deleted": employee_id}
```

Both `/admin/stats` and `/admin/employees/{id}` will require authentication without you having to add `Depends(get_current_user)` to each function individually.

***

## `include_router` Options

You can override or extend router settings when registering:

```python theme={null}
app.include_router(
    employees.router,
    prefix="/api/v1",          # Override or add an additional prefix
    tags=["v1 - Employees"],   # Override the tags
    deprecated=True            # Mark all routes in this router as deprecated
)
```

This is useful for API versioning — you can include the same router twice under `/api/v1` and `/api/v2` with different prefixes.
