Quickstart
The TL;DR for getting started with Air.
Installation
To start a new Air project, create a directory and set up your environment:
mkdir helloair
cd helloair
uv venv
source .venv/bin/activate
uv init
uv add air
uv add "fastapi[standard]"
Note
You can also do:
pip install -U air "fastapi[standard]"
or even
conda install air -c conda-forge
conda install "fastapi[standard]" -c conda-forge
Hello, Air! Example
Create a main.py
file in your new directory with:
import air
app = air.Air()
@app.get("/")
async def index():
return air.layouts.mvpcss(
air.H1("Hello, Air!"),
air.P("Breathe it in.")
)
Serve your app with:
fastapi dev
Open your page by clicking this link: http://localhost:8000/
Here's a few interesting things about this page:
- The page has an attractive layout and typography
- The Python for this app is similar in design to how FastAPI code is written
- If you typed the code out in an IDE with intellisense, you'll have seen every Air object includes useful instruction. Air is designed to be friendly to both humans and LLMs, hence every object is carefully typed and documented
Routing
Routing is how users on paths are directed to the correct 'view' function that handles their request.
Basics
Air wraps FastAPI so you can use the same decorator patterns for specifying URLs:
import air
app = air.Air()
@app.get("/")
def index():
return air.layouts.mvpcss(
air.H1("Hello, Air!"),
air.P("Breathe it in.")
)
@app.get("/air-is-grounded")
def air_is_grounded():
return air.layouts.mvpcss(
air.H1("Air is Grounded"),
air.P("Built on industry standard libraries including:"),
air.Ul(
air.Li('FastAPI'),
air.Li('Starlette'),
air.Li('Pydantic'),
air.Li('Jinja'),
)
)
@app.post('/form-handler')
async def form_handler(request: air.Request): # (1)!
...
- Form handling in Air requires
async
functions and usually anair.Request
argument. We cover forms later on this page as well as in numerous places across the Air documentation.
app.page decorator
To expedite HTTP GET
pages we provide the app.page
decorator, which can replace the app.get()
decorator for views without arguments. app.page
converts the name of the function to the route, converting underscores to dashes:
import air
app = air.Air()
@app.page # Renders as '/'
def index(): # (1)!
return air.layouts.mvpcss(
air.H1("Hello, Air!"),
air.P("Breathe it in.")
)
@app.page # Renders as '/air-is-grounded'
def air_is_grounded(): # (2)!
return air.layouts.mvpcss(
air.H1("Air is Grounded"),
air.P("Built on industry standard libraries including:"),
air.Ul(
air.Li('FastAPI'),
air.Li('Starlette'),
air.Li('Pydantic'),
air.Li('Jinja'),
)
)
app.page
used over functions namedindex
are converted to the/
route.app.page
used over functions are converted to a route based on their name, with underscores converted to dashes.
Variables in Paths
Variables can be added to URLs by marking them in curly braces like {variable}
in the application.get
, application.post
, application.put
, and application.delete
function decorators. The function receives the {variable}
so long as it is the correct type specified by the function.
import air
app = air.Air()
@app.get('/users/{username}') # (1)!
def user_detail(username: str): # (2)!
return air.layouts.mvpcss(
air.Title(username),
air.H1(username)
)
- We've specified a variable called
username
. - We have defined a function argument named
username
, which is identical to the variable specified in the decorator. We also specified the Python type in this definition.
Try it out by going to http://localhost:8000/users/Aang
Variables in URLs
If you specify variables in in the function definition but not the function decorator, those become URL parameters.
The function receives the {variable}
so long as it is the correct type specified by the function.
import air
app = air.Air()
@app.get('/users')
def user_detail(username: str): # (1)!
return air.layouts.mvpcss(
air.Title(username),
air.H1(username)
)
- We have defined a function argument named
username
, which is identical to the variable specified in the decorator. We also specified the Python type in this definition.
Try it out by going to http://localhost:8000/users/?username=Aang
Air Tags
Air Tags are one of Air's two ways to generate HTML output. They are useful for keeping file size down, general HTML delivery, and especially with fragment responses via HTMX.
JavaScript Files
Using Air Tags to call external JavaScript files:
import air
app = air.Air()
@app.page
def index():
return air.Script(src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js")
Inline Scripts
When you need to use JavaScript inline in Air Tags:
import air
app = air.Air()
@app.page
def index():
return air.Script("alert('The Last Airbender is an awesome series.')")
CSS Files
Here's how to use Air Tags to call external CSS files:
import air
app = air.Air()
@app.page
def index():
return air.Html(
air.Head(
air.Link(rel="stylesheet", href="https://unpkg.com/mvp.css"),
),
air.Body(
air.Main(
air.H1("Air Web Framework"),
air.P("The web framework for Air Nomads.")
)
)
)
Inline CSS Styles
Inline CSS styles via Air are a good way to control design elements at runtime.
import air
app = air.Air()
@app.page
def index():
return air.Html(
air.Head(
air.Style("h1 {color: red;}"),
),
air.Body(
air.H1("Air Web Framework"),
air.P("The web framework for Air Nomads.")
)
)
Want to learn more?
Check out these documentation sections:
Future Segments
What we plan to include in the Quick Start:
- Jinja
- The Jinja + Air Tags pattern the core devs love to use
- Forms
- HTMX basics
- Routing: Variables in URLs
- Routing: Variables in paths
- Custom exception handlers
- Sessions
- Cookies
- File uploads (part of forms)
- Large File downloads
- Server Sent Events