# How to Use the `.url()` Method for URL Generation in Air Routes

> Learn to generate URLs in Air routes using the .url() method. Easily perform reverse URL lookup with optional query parameters for your web applications.

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

---

**The `.url()` method is automatically attached to every route-decorated function in Air, enabling reverse URL lookup with optional query parameters via `func.url(**path_params, query_params={})`.**

Air's routing layer provides a powerful URL generation helper that eliminates hardcoded paths in your web applications. When you register a function using route decorators like `@app.get()` or `@app.post()`, the framework automatically injects a `.url()` method for reverse URL resolution. This feature leverages FastAPI/Starlette's underlying path resolution while adding convenient query parameter support.

## How the `.url()` Method Works

### Route Decoration and Helper Injection

According to the source code in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py), the `RouterMixin._route` method attaches the URL helper at line 308:

```python
decorated.url = self._url_helper(name or getattr(func, "__name__", "unknown"))

```

This assignment occurs immediately after the function is decorated, ensuring every route handler carries the `.url()` capability regardless of HTTP verb.

### URL Building Implementation

The `_url_helper` method (lines 381-390) constructs the final URL by calling `url_path_for()` and conditionally appending query strings:

```python
def helper_function(**params: Any) -> str:
    query_params = params.pop("query_params", None)
    path = self.url_path_for(name, **params)
    if query_params is None:
        return path
    query_string = urlencode(query_params, doseq=True)
    return f"{path}?{query_string}" if query_string else path

```

This implementation supports both **path parameters** (passed as keyword arguments) and **query parameters** (passed via the special `query_params` dictionary).

## Generating URLs for GET Routes

### Basic Reverse Lookup

To generate a URL for a simple GET endpoint, call `.url()` on the decorated function with the required path parameters:

```python
import air

app = air.Air()

@app.get("/users/{user_id}")
def get_user(user_id: int):
    # URL to this same endpoint:

    return air.H1(f"URL: {get_user.url(user_id=42)}")   # → "/users/42"

```

As implemented in `feldroy/air`, this pattern works because `@app.get` triggers the helper injection in `RouterMixin._route`.

### Cross-Route URL Generation

You can generate links to other routes within your application by calling `.url()` on the target function:

```python
@app.get("/profile/{username}")
def profile_page(username: str):
    return air.H1(f"Profile page for {username}")

@app.get("/")
def index():
    # Generate a link to the profile page

    return air.H1(f"Link: {profile_page.url(username='johndoe')}")
    # → "/profile/johndoe"

```

This pattern is validated in the test suite under `test_air_router_get_url_method_different_path` (lines 75-82 of [`tests/test_routing.py`](https://github.com/feldroy/air/blob/main/tests/test_routing.py)).

## Working with POST Routes and Form Actions

The `.url()` method works for **any HTTP verb**, including POST, PUT, PATCH, and DELETE. This is particularly useful for setting form actions dynamically:

```python
@app.post("/submit")
async def submit_form():
    return air.H1("Submitted!")

@app.get("/form")
def form_page():
    # Use the POST route's URL as the form action

    return air.Form(..., method="post", action=submit_form.url())

```

The test `test_air_router_post_with_url_method` (lines 68-74 of [`tests/test_routing.py`](https://github.com/feldroy/air/blob/main/tests/test_routing.py)) confirms this behavior works identically across all HTTP methods.

## Adding Query Parameters

To append query strings to generated URLs, pass a dictionary to the `query_params` argument:

```python
@app.get("/items/{item_id}")
def get_item(item_id: int, page: int = 1):
    return air.P(f"Item {item_id}, page {page}")

# Build a URL with a query string:

url = get_item.url(item_id=5, query_params={"page": 2, "tags": ["a", "b"]})

# → "/items/5?page=2&tags=a&tags=b"

```

The `urlencode` function handles sequence values (like lists) using `doseq=True`, ensuring proper serialization of multi-value parameters.

## Error Handling for Missing Parameters

If you omit required path parameters, the method raises `NoMatchFound`:

```python
@app.get("/item/{item_id}")
def item_detail(item_id: int):
    ...

# Missing required path param raises NoMatchFound

try:
    bad_url = item_detail.url()
except air.router.NoMatchFound:
    print("Missing parameters!")

```

This validation is tested in `test_air_router_get_with_url_method_throws_error` (lines 52-60 of [`tests/test_routing.py`](https://github.com/feldroy/air/blob/main/tests/test_routing.py)).

## Summary

- **Automatic injection**: Every route decorator (`@app.get`, `@app.post`, etc.) attaches `.url()` via `_url_helper()` in [`src/air/routing.py`](https://github.com/feldroy/air/blob/main/src/air/routing.py) (line 308).
- **Reverse lookup**: Call `function.url(**path_params)` to generate the path string for any registered route.
- **Query support**: Pass `query_params={"key": "value"}` to append URL-encoded query strings.
- **Universal compatibility**: Works with all HTTP verbs and route types defined in the Air framework.
- **Error safety**: Missing required parameters raise `NoMatchFound` exceptions for debugging.

## Frequently Asked Questions

### How do I access the `.url()` method on a route function?

The `.url()` method is available immediately after decoration. Simply reference the function name followed by `.url()`:

```python
@app.get("/users/{id}")
def user_detail(id: int): ...

# Access anywhere in your code

link = user_detail.url(id=42)

```

### Can I use `.url()` with POST, PUT, and DELETE routes?

Yes. The method attaches to any function decorated with route handlers regardless of HTTP verb. This is particularly useful for generating form actions pointing to POST endpoints or links to DELETE confirmation pages.

### How do I add query strings to URLs generated with `.url()`?

Pass a dictionary to the special `query_params` keyword argument:

```python
url = my_route.url(id=1, query_params={"sort": "date", "order": "desc"})

```

The framework automatically URL-encodes the dictionary and appends it to the generated path.

### What happens if I forget to provide required path parameters?

The method raises `air.router.NoMatchFound`. You should wrap URL generation in try-except blocks when parameters are dynamically constructed or potentially incomplete:

```python
try:
    url = route.url()
except air.router.NoMatchFound:
    url = "/fallback-path"

```