# How to Organize Routes in Multiple Files Using AirRouter

> Learn to organize routes in multiple files with AirRouter. Keep your codebase modular and maintainable by defining path operations in separate modules and registering them with your app.

- Repository: [Feldroy/air](https://github.com/feldroy/air)
- Tags: how-to-guide
- Published: 2026-03-01

---

**Use `air.AirRouter()` to define path operations in separate modules, then register them with your main application via `app.include_router()` to keep your codebase modular and maintainable.**

Air is a lightweight wrapper around FastAPI that provides the `AirRouter` class specifically for organizing routes across multiple files. By leveraging this pattern, you can group related endpoints into dedicated modules—such as [`users.py`](https://github.com/feldroy/air/blob/main/users.py) or [`blog.py`](https://github.com/feldroy/air/blob/main/blog.py)—while maintaining full compatibility with FastAPI's routing system as implemented in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py). This approach keeps your main application file clean and makes large projects easier to navigate and test.

## Understanding the AirRouter Pattern

`AirRouter` inherits from FastAPI's `APIRouter` and is defined in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py)【/cache/repos/github.com/feldroy/air/main/src/air/routing.py#L395-L433】. It allows you to create isolated groups of routes that can be configured with shared prefixes, tags, and dependencies before being attached to the main `Air` application.

The typical workflow involves four steps:

1. Instantiate `air.AirRouter()` with optional configuration (prefix, tags, etc.)
2. Define routes using decorators like `@router.get()` or `@router.page()`
3. Export the router from the module
4. Import and register the router using `app.include_router()`

## Creating Modular Route Files

### Define a User Router with Prefixes

Create a dedicated file for user-related endpoints and configure a URL prefix at the router level:

```python

# users.py

import air

router = air.AirRouter(prefix="/users", tags=["users"])

@router.get("/")
def list_users() -> air.H1:
    """GET /users – list all users."""
    return air.H1("User list")

@router.get("/{user_id}")
def get_user(user_id: int) -> air.H1:
    """GET /users/{user_id} – show a single user."""
    return air.H1(f"User {user_id}")

@router.post("/")
def create_user() -> air.H1:
    """POST /users – create a new user."""
    return air.H1("User created")

__all__ = ["router"]

```

The `prefix="/users"` argument ensures all routes in this module automatically mount under `/users`, while `tags=["users"]` groups them in the auto-generated OpenAPI documentation.

### Configure Automatic Page Routes

For HTML page routes that derive their URL path from function names, use the `@router.page` decorator with a custom `path_separator`:

```python

# blog.py

import air

router = air.AirRouter(
    prefix="/blog", 
    tags=["blog"], 
    path_separator="-"
)

@router.page  # automatic route → "/blog"

def index() -> air.H1:
    return air.H1("Blog front page")

@router.page  # function name becomes "/blog/recent-posts"

def recent_posts() -> air.H1:
    return air.H1("Recent posts")

```

The `path_separator` parameter (handled in `AirRouter.__init__` at lines 333-340 of [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py)) controls how function names are transformed into URL paths when using the automatic `page` routing feature【/cache/repos/github.com/feldroy/air/main/src/air/routing.py#L333-L340】.

## Wiring Routers to the Main Application

Import your modular routers into the main application file and attach them using `include_router()`. This method is forwarded to the underlying FastAPI application in [`src/air/applications.py`](https://github.com/feldroy/air/blob/main/src/air/applications.py):

```python

# app.py

import air
from users import router as users_router
from blog import router as blog_router

app = air.Air()

# Attach the modular routers

app.include_router(users_router)   # mounts under /users

app.include_router(blog_router)    # mounts under /blog

# Define routes directly on the app if needed

@app.page
def home() -> air.H1:
    return air.H1("Welcome to the Air demo")

```

Running this application with `uvicorn app:app` exposes the following routes:

- `/` (GET) → `home`
- `/users/` (GET) → `list_users`
- `/users/{user_id}` (GET) → `get_user`
- `/users/` (POST) → `create_user`
- `/blog` (GET) → `index`
- `/blog/recent-posts` (GET) → `recent_posts`

## Repository Examples and Testing

The [`examples/src/routing__RouterMixin__page.py`](https://github.com/feldroy/air/blob/main/examples/src/routing__RouterMixin__page.py) file provides a minimal working demonstration of this pattern【/cache/repos/github.com/feldroy/air/main/examples/src/routing__RouterMixin__page.py#L1-L22】:

```python
import air

app = air.Air()
router = air.AirRouter()

@app.page
def index() -> air.H1:  # → "/"

    return air.H1("I am the home page")

@router.page
def data() -> air.H1:    # → "/data"

    return air.H1("I am the data page")

@router.page
def about_us() -> air.H1:  # → "/about-us"

    return air.H1("I am the about page")

app.include_router(router)

```

For comprehensive usage patterns—including prefix handling, custom separators, and dependency injection—refer to the test suite in [`tests/test_routing.py`](https://github.com/feldroy/air/blob/main/tests/test_routing.py), which validates the full router lifecycle from definition to inclusion.

## Summary

- **Create routers in separate files** using `router = air.AirRouter()` with optional `prefix` and `tags` parameters
- **Define routes** using standard decorators (`@router.get()`, `@router.post()`, `@router.page()`) that mirror FastAPI's API
- **Export and import** the router instances into your main application file to keep the entry point clean
- **Register with `include_router()`** to mount the routes at their configured prefixes
- **Customize behavior** via `path_separator` for automatic page routes or shared `dependencies` for middleware-like functionality

All routing logic is implemented in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py), with the main application integration handled in [`src/air/applications.py`](https://github.com/feldroy/air/blob/main/src/air/applications.py), ensuring full FastAPI compatibility while simplifying multi-file organization.

## Frequently Asked Questions

### Does AirRouter support all FastAPI APIRouter features?

Yes. Because `AirRouter` inherits directly from FastAPI's `APIRouter` class, it supports all standard features including route-specific dependencies, response models, exception handlers, and custom route classes. You can use `dependencies`, `responses`, and `default_response_class` parameters exactly as you would in standard FastAPI applications.

### How do I change the URL path separator for automatic page routes?

Pass the `path_separator` argument when creating the router: `air.AirRouter(path_separator="-")`. This converts function names like `about_us` into URL paths like `/about-us` instead of the default `/about_us`. This configuration is processed in the `AirRouter.__init__` method around line 333 of [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py).

### Can I nest AirRouters within other AirRouters?

While FastAPI supports nested routers, the Air framework typically expects you to register all routers directly with the main `Air` application via `app.include_router()`. This flat structure keeps the routing table explicit and easier to debug, though you can technically include a router in another router before attaching it to the app if you need hierarchical organization.

### Where is the AirRouter class defined in the source code?

The `AirRouter` class is defined in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) between lines 395 and 433【/cache/repos/github.com/feldroy/air/main/src/air/routing.py#L395-L433】, alongside the `AirRoute` class and the `page` helper method. This file also contains the `RouterMixin` that provides the `include_router()` method used by the main `Air` application class.