The Stario way

The problems I kept hitting, and the shape that came out of them.

I made Stario to get rid of frustrations I kept meeting in Python web work. If you recognise the problems, you will probably like the solution. If you do not, you probably will not, and that is fine.

The problems#

I spent about ten years as a backend engineer. Then I started building whole applications end to end, alone, and I noticed how much of a default stack I did not understand and did not need.

A request came in through an ASGI server I had not written, into a framework that turned it into an object I could not see the bytes of, through a dependency injection system, into a function that returned another object, which a different layer turned back into bytes. When something broke at three in the morning, I had five layers to read and none of them were mine.

HTML lived in a second language. Template engines have their own loops, their own filters, their own idea of scope. Every one I used was a smaller, stranger Python.

Realtime needed a different stack. WebSockets, a message format, a client-side state store, a build step. The moment one page needed to update by itself, the whole project changed shape.

And the tools were quiet. A test passed or a log line printed. What happened inside a request, how long each step took, which route it was: I had to add that myself, every time.

The choices#

Each piece of Stario is an answer to one of those problems.

The handler writes bytes. It is async def handler(c, w): it gets the request and a writer, and what it writes is what goes on the wire. There is no response object to construct and no later layer that turns it into HTTP. Stario includes its own HTTP server, so nothing sits between your function and the socket that you did not put there. The cost is that you say what you mean; w.html(page) is one line, but it is your line, and the status code is visible in it.

A route is a value. Route("GET /users/{id}") is a Python object. You register it, you build links from it with href(), and you test with it. There is no decorator and no url_for("name"); the route is the name. A misspelled link is a NameError at import time, not a 404 in production.

HTML is Python. h.Div(...) returns a tree, and a view is a function that returns one. Loops, conditions, and reuse come from the language you already know. There is no second scope and no file that is almost but not quite Python.

A live page is one open response. A handler can keep writing for as long as the browser stays. Server-sent events are plain HTTP, and Datastar, a small library in the page, morphs the HTML the server sends into the page. Realtime becomes something you add to one page, not a different architecture. Live pages is the full argument.

Telemetry is built in. Every request is a span. Startup is a span. You print them to the terminal while you develop and write them to SQLite in production, and then you ask questions of your own traffic with SQL. Telemetry design explains the shape.

What Stario is not#

Stario is not ASGI. asyncio.run(serve(bootstrap)) is the whole entry point, so python main.py runs the server and uvx watchfiles 'python main.py' . gives you hot reload. It does not mount in Uvicorn.

Stario is not batteries-included. There is no ORM, no admin, no form library, no validator. Use the libraries you like; the how-tos show where each plugs in.

Stario is not trying to hide the web from you. The bet is that HTTP, HTML, and a stream are not that complicated once you can see them. Learn them with Stario, and you keep them when you leave.

Where this comes from#

Datastar shaped how Stario treats the browser. Its guide, The Tao of Datastar, argues for sending HTML, keeping state on the server, and letting the browser do what the browser does well. Stario is the Python side of that argument.

Yard came a year later, for the same reason. I had three small apps and one VM, and every deploy tool I tried wanted a pipeline, a registry, and a dashboard. I wanted git push. Yard is that.

To see the shape in code, Realtime tiles is the whole idea in one file. To start smaller, Hello world is one route.