# How the AirRoute Class Customizes FastAPI Routing in Air

> Discover how AirRoute enhances FastAPI routing by transforming requests, resolving type annotations, and adding reverse URL helpers. Learn more about this powerful customization.

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

---

**AirRoute is a thin wrapper around FastAPI's APIRoute that transforms raw requests into AirRequest objects, resolves postponed type annotations, and equips endpoints with reverse-URL helpers while maintaining full FastAPI compatibility.**

The `AirRoute` class in the [feldroy/air](https://github.com/feldroy/air) repository provides the foundation for Air's "FastAPI-compatible but Air-enhanced" routing layer. By subclassing FastAPI's `APIRoute`, it seamlessly integrates Air-specific request handling, automatic HTML tag rendering, and convenient URL generation without sacrificing any native FastAPI functionality.

## What is AirRoute?

`AirRoute` is defined in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) as a specialized subclass of FastAPI's `APIRoute` class. Unlike standard FastAPI routes that work directly with Starlette requests, `AirRoute` acts as an interceptor that prepares the execution environment before your handler runs and processes the return value afterward.

When you create an application using `air.Air()` or `air.AirRouter()`, every route automatically uses `AirRoute` as the underlying route class. This happens because `AirRouter.__init__` passes `route_class=AirRoute` to FastAPI's `APIRouter` constructor in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) (lines 452-461).

## Core Customization Features

### Extending APIRoute for Air-Specific Behavior

At its core, `AirRoute` inherits all standard FastAPI routing behavior while overriding specific hooks to inject Air functionality. The class definition at line 98 of [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) shows the inheritance structure:

```python
class AirRoute(APIRoute):
    # Custom initialization and handler logic

```

This inheritance ensures that existing FastAPI features like dependency injection, OpenAPI schema generation, and automatic validation continue to work exactly as expected.

### Resolving Postponed Type Annotations (PEP 563)

One critical customization happens during route initialization. Modern Python uses postponed annotation evaluation (PEP 563), which can break runtime type inspection that Air relies on for proper request handling and response serialization.

In [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) (lines 101-117), the `__init__` method rebuilds the endpoint's signature with resolved annotations:

```python

# Inside AirRoute.__init__

# Resolves 'AirRequest' and tag return types from string annotations

# to actual types at import time

```

This ensures that when you import Air modules with `from __future__ import annotations`, the framework still correctly identifies `AirRequest` parameter types and HTML tag return types for proper handling.

### Wrapping Requests with AirRequest

The most visible customization occurs in the `custom_route_handler` method (lines 122-128 of [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py)). Before your endpoint function executes, `AirRoute` creates an `AirRequest` instance and passes it to your handler instead of the raw Starlette request:

```python
async def custom_route_handler(self, request: Request) -> Response:
    # Wrap the incoming request

    air_request = AirRequest(request)
    # Forward to the original handler with the enhanced request object

    return await original_handler(air_request)

```

This wrapping provides convenient methods like `await request.form()` and other Air-specific request utilities defined in [`src/air/requests.py`](https://github.com/feldroy/air/blob/main/src/air/requests.py), giving you a richer API than standard FastAPI requests while maintaining backward compatibility.

### Enabling Automatic AirResponse Rendering

While the actual wrapping logic for return values lives in `RouterMixin._wrap_endpoint`, `AirRoute` enables automatic HTML rendering by signaling to FastAPI that it should use this custom route class. When your endpoint returns Air tags (like `air.H1` or `air.Div`), the framework automatically converts these to proper HTTP responses via `AirResponse` from [`src/air/responses.py`](https://github.com/feldroy/air/blob/main/src/air/responses.py).

This means you can write:

```python
@app.get("/")
def home() -> air.H1:
    return air.H1("Welcome")

```

And receive properly formatted HTML without manually constructing Response objects.

### Adding Reverse-URL Generation via url()

Every route created through `AirRoute` receives a `.url()` method for programmatic URL generation. In [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) (lines 108-110), the `_route` method attaches this helper:

```python
decorated.url = self._url_helper(...)

```

This allows you to generate URLs for any endpoint from within your code or templates:

```python

# Generates "/hello/world" based on the route pattern

greet.url(name="world")

```

The actual URL construction logic resides in `RouterMixin._url_helper`, but `AirRoute` ensures every decorated function gets this convenient attribute.

## How AirRoute Integrates with AirRouter

The `AirRouter` class (also in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py)) serves as the main entry point that wires everything together. When you initialize an `AirRouter`, it automatically configures FastAPI to use `AirRoute` for all route registration:

```python

# In AirRouter.__init__ (src/air/routing.py, lines 452-461)

super().__init__(
    route_class=AirRoute,  # This is the key integration point

    # ... other FastAPI APIRouter parameters

)

```

 Developers never need to mention `AirRoute` explicitly in their code. By using `air.Air()` or `air.AirRouter()`, you automatically get Air's enhanced routing behavior throughout your entire application.

## Practical Implementation Example

The following example from [`examples/src/simple_air_app.py`](https://github.com/feldroy/air/blob/main/examples/src/simple_air_app.py) demonstrates how `AirRoute` works in practice:

```python
import air

app = air.Air()  # Creates FastAPI app with AirRoute as default

@app.get("/hello/{name}")
def greet(name: str) -> air.H1:
    """A basic route that returns an Air tag."""
    return air.H1(f"Hello, {name}!")

# The handler receives an AirRequest, not a raw Starlette request.

# The returned Air tag is automatically rendered as HTML via AirResponse.

# Reverse-URL generation:

print(greet.url(name="world"))  # Output: /hello/world

```

When you run this with Uvicorn and inspect the application routes, you'll find that `app.routes[0].__class__` returns `AirRoute`, confirming that the customization is active. The request object passed to `greet` is actually an `AirRequest` instance, giving you access to Air's form handling and other request utilities.

## Key Files Supporting AirRoute

Several modules work together to enable `AirRoute` functionality:

- **[`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py)** – Contains `AirRoute`, `RouterMixin`, and `AirRouter` definitions
- **[`src/air/requests.py`](https://github.com/feldroy/air/blob/main/src/air/requests.py)** – Implements `AirRequest` used by the custom route handler
- **[`src/air/responses.py`](https://github.com/feldroy/air/blob/main/src/air/responses.py)** – Provides `AirResponse` for automatic tag rendering
- **[`src/air/utils.py`](https://github.com/feldroy/air/blob/main/src/air/utils.py)** – Supplies `cached_signature`, `cached_unwrap`, and `default_generate_unique_id` helpers for signature fixing and unique ID generation
- **[`src/air/exception_handlers.py`](https://github.com/feldroy/air/blob/main/src/air/exception_handlers.py)** – Default 404 handler referenced by `AirRouter`

## Summary

- **AirRoute subclasses FastAPI's APIRoute** in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) to inherit standard routing while adding Air-specific enhancements.
- **Postponed annotations are resolved at initialization** (lines 101-117) ensuring PEP 563 compatibility for runtime type checking.
- **Requests are automatically wrapped as AirRequest objects** via `custom_route_handler` (lines 122-128), providing enhanced request utilities.
- **Return values automatically render as HTML** through integration with `AirResponse` and `RouterMixin._wrap_endpoint`.
- **Every endpoint gets a `.url()` method** (lines 108-110) for programmatic URL generation without manual route naming.
- **Zero configuration required** – `AirRouter` sets `route_class=AirRoute` by default, making the customization transparent to developers.

## Frequently Asked Questions

### What is the difference between AirRoute and FastAPI's APIRoute?

**AirRoute extends APIRoute to provide Air-specific request handling.** While `APIRoute` passes raw Starlette requests to your handlers, `AirRoute` wraps them as `AirRequest` objects and handles the resolution of postponed type annotations. It also attaches reverse-URL helpers to endpoints, features that standard FastAPI routes do not provide by default.

### Do I need to manually specify AirRoute when creating routes?

**No, AirRoute is automatically applied when you use Air's router classes.** The `AirRouter` class in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) passes `route_class=AirRoute` to FastAPI's `APIRouter` constructor by default. Whether you use `@app.get()` on an `Air()` instance or create routes through `AirRouter`, the framework handles the `AirRoute` assignment transparently.

### How does AirRoute handle type annotations with postponed evaluation?

**AirRoute resolves string annotations during initialization.** In the `__init__` method (lines 101-117 of [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py)), it rebuilds the endpoint's signature to resolve postponed annotations (PEP 563) into actual types. This ensures that when you use `from __future__ import annotations`, Air can still correctly identify `AirRequest` parameters and HTML tag return types at runtime.

### Can I use regular FastAPI dependencies and validation with AirRoute?

**Yes, AirRoute maintains full FastAPI compatibility.** Because `AirRoute` inherits from `APIRoute`, all standard FastAPI features including dependency injection, `Depends()`, query parameter validation, and OpenAPI schema generation continue to work exactly as they do in standard FastAPI applications. The customizations only add Air-specific functionality without removing any FastAPI capabilities.