Air Web Framework

repository·main·Indexed 21 days ago

https://github.com/feldroy/air

A Python web framework designed for AI-assisted development, built on FastAPI, Starlette, Pydantic, and HTMX. Air supports building HTML via traditional Jinja templates or typed Python classes called Air Tags. It features a CLI for running applications, support for both sync and async route handlers, and tools for containerization and Kubernetes deployment.

Tokens
43.7K
Snippets
168
Records
226
Agent score
75%

What's inside Air

  1. Air API Reference Overview

    main

    The Air API provides a comprehensive set of tools for building web applications. Key functional areas include:

    • Application Lifecycle: Use Applications to instantiate your app.
    • Data Handling: Use Forms for user data validation and Requests (an HTMX utility) for dependency injection.
    • UI & Templating: Leverage Tags (HTML), SVG tags, Layouts (including mvcss and picocss support), and Templating (Jinja and Air Tag renderers).
    • Server Logic: Manage Routing for app composition, Middleware for request processing, and Background Tasks for asynchronous work.
    • Communication: Handle Responses via AirResponse or SSEResponse (Server Sent Events), and manage errors via Exception Handlers and specific Exceptions.
  2. Combine multiple Air apps using AirRouter

    main
    Introduced in version 0.27.0, AirRouter allows you to combine multiple Air applications into a single unit. This is useful for managing multiple apps that need to share the same middleware and dependencies.
  3. Combine multiple Air apps using AirRouter

    main

    Use air.AirRouter() to create a router that can be included into a main air.Air() application. This allows you to modularize your application by knitting together several Python modules, each with its own Air views. Routers allow for the sharing of sessions and other application states across the included modules.

    To use a router:

    1. Instantiate air.AirRouter() in a module.
    2. Define pages using the @router.page decorator.
    3. Include the router in your main app using app.include_router(router_instance).
    # cart.py
    import air
    
    router = air.AirRouter()
    
    @router.page
    def cart():
        return air.H1("I am a shopping cart")
    
    # main.py
    import air
    from cart import router as cart_router
    
    app = air.Air()
    app.include_router(cart_router)
    
    @app.page
    def index():
        return air.H1("Home page")
  4. How Air layouts work

    main

    Air layouts simplify HTML document creation by automatically sorting tags into the correct locations (<head> or <body>). Instead of manually nesting air.Html, air.Head, and air.Body tags, you can pass a flat list of tags to a layout function. Air uses an intelligent filtering system to separate them:

    • Head tags: Title, Style, Meta, Link, Script, and Base are automatically moved to the <head> section.
    • Body tags: All other tags are automatically moved to the <body> section.

    This allows you to mix head and body tags freely in your code, making your route handlers much cleaner.

    @app.get("/")
    def home():
        return air.layouts.mvpcss(
            # Mix head and body tags freely - Air sorts them
            air.Title("Dashboard"),
            air.H1("Welcome to the Dashboard"),
            air.Meta(name="description", content="User dashboard"),
            air.P("Your stats here"),
            air.Script(src="dashboard.js"),
        )
  5. Understand Air Tags and their documentation

    main

    An Air Tag is a core abstraction in Air. For detailed conceptual explanations of how they work, refer to the concepts document about tags.

    Because Air Tags often correspond directly to HTML elements, their documentation is split across several specialized pages to ensure efficient compilation. If you are looking for documentation on a specific tag that maps to an HTML element, consult the categorized lists:

    • A-D
    • E-M
    • N-S
    • T-Z
  6. When to use Air

    main

    Air is an experimental, highly-unstable Python web framework that is currently in a pre-launch state. It is not enterprise-ready and every release may contain breaking changes.

    Use Air if you want:

    • Fast development: Intuitive shortcuts and optimizations for coding HTML with FastAPI.
    • FastAPI Integration: Designed to serve both APIs and web pages from a single application.
    • Air Tags: Performant HTML content generation using Python classes.
    • Jinja Compatibility: Seamlessly mix Jinja templates with Air Tags in the same view without manual HtmlResponse boilerplate.
    • HTMX Support: Built-in utilities designed for HTMX workflows.
    • Pydantic-powered Form Validation: Use Pydantic for HTML form validation via dependency injection or within views.
    • First-class SVG support: Access SVGs via the air.svgs namespace.
    • Ecosystem participation: To contribute to the growing Air ecosystem and core package.
  7. Leverage Air Tags for AI-assisted development

    main
    The Air Tag API is a core feature designed to be highly compatible with IDEs and Large Language Models (LLMs). The API is considered stable, with a focus on providing helpful docstrings and examples to assist both human developers and AI coding agents in constructing UI components.
  8. Integrate Jinja templates into Air Tag trees

    main

    As of version 0.46.0, the JinjaRenderer supports an as_string=True option. This allows you to render Jinja templates as strings and embed them directly into Air tag trees, enabling an incremental migration from Jinja-based views to Air Tags.

    # Example pattern enabled by JinjaRenderer(as_string=True)
    # Rendered Jinja content can be embedded directly in Air tag trees
  9. How Air Tags work and render HTML

    main

    Air Tags are Python classes used to generate HTML. They can be nested to create complex web pages or small components.

    Rendering Methods

    Every Air Tag provides several ways to convert the Python object into an HTML string:

    • render(): Returns the HTML representation of the tag and its children.
    • str(): A shortcut for the render() method.
    • print(): Converts the tag to a string and sends it to stdout.
    • .pretty_render(): Returns a human-friendly, indented HTML string (useful for debugging).

    When returned from an Air view (e.g., in FastAPI), the conversion to HTML happens automatically.

    Example

    from air import H1, Article, P
    
    content = Article(
        H1("Air Tags"),
        P(
            "Air Tags are a fast, expressive way to generate HTML.",
            class="subtitle",
        ),
    )
    
    # Renders the full HTML structure
    print(content.pretty_render())
    from air import H1, Article, P
    
    content = Article(
        H1("Air Tags"),
        P(
            "Air Tags are a fast, expressive way to generate HTML.",
            class="subtitle",
        ),
    )
    print(content.render())
  10. HTTP HEAD request support in Air

    main

    As of version 0.48.1, Air restores standard Starlette-like behavior where every GET route automatically responds to HTTP HEAD requests with a 200 status, correct headers, and an empty body. This applies to the following route decorators:

    • @app.page
    • @app.get
    • @router.page
    • @router.get

    Previously, these routes would return a 405 Method Not Allowed error because FastAPI's routing layer does not automatically add HEAD to GET routes.

  11. Use sync route handlers in Air

    main

    Air supports both asynchronous (async def) and synchronous (def) route handlers. When you define a handler using plain def, Air runs it in a Starlette threadpool. This allows you to perform blocking operations like database queries or file I/O directly within your handler without blocking the main event loop or requiring async/await syntax.

    # Synchronous handler (runs in a threadpool)
    @app.page
    def my_sync_page(request: air.Request):
        # Blocking I/O is safe here
        data = db.fetch_all("SELECT * FROM table")
        return air.div(air.text(f"Data: {data}"))
    
    # Asynchronous handler
    @app.page
    async def my_async_page(request: air.Request):
        data = await db.fetch_all("SELECT * FROM table")
        return air.div(air.text(f"Data: {data}"))
  12. Understand Air's relationship with FastAPI and Starlette

    main

    Air is built on top of FastAPI, which in turn is built on top of Starlette. Because of this architecture, Air is essentially FastAPI with enhanced features for working with HTML and HTMX.

    Key takeaway for developers: Anything you can do with FastAPI or Starlette, you can also do with Air. Air provides a high-level layer for web page generation while maintaining the full capabilities of the underlying ASGI toolkit.