Index
A msgspec-first ASGI micro-framework for Python 3.13+.
uv add jero
What is jero?¶
jero is an AI-powered (a note on AI usage), msgspec-first ASGI framework where your type hints are the API contract. Routing, binding, validation, serialization, auth checks, and OpenAPI generation all derive from statically declared types, introspected once at startup. The request path stays minimal: dict lookup, msgspec decode, handler call, encode.
There are no route decorators and no dependency-injection container. Routes are plain
classes (Resource for REST collections, Endpoint for one-off routes), the method
name is the HTTP operation, and dependencies are ordinary constructor arguments.
Everything that crosses the wire is a msgspec Struct: bodies, headers, path and
query params, forms. That single contract drives validation, serialization, startup
checks, schema generation, and msgspec's compiled-codec speed.
Quickstart¶
from msgspec import Struct
from jero import BaseApp, Resource
class WidgetPath(Struct):
widget_id: str
class Widget(Struct):
id: str
name: str
class WidgetResource(Resource, path="/widgets"):
# called as: GET /widgets/{widget_id}
async def read_one(self, path: WidgetPath) -> Widget:
return Widget(id=path.widget_id, name="widget-name")
class App(BaseApp):
async def wire(self) -> None:
self._include_resource(WidgetResource())
app = App()
No @app.get(...), no runtime route discovery: the class declares the path, and the
method name declares the operation.
Run it under any ASGI server, e.g. granian:
granian --interface asgi myapp:app
New here? Start with Getting Started.
jero is fast¶
jero was written for speed and performance: in our four-scenario benchmark against seven frameworks across Python, Go, and Bun, jero is the fastest Python framework in every one, methodology included. → Performance
Core principles¶
jero makes one bet: being aggressively prescriptive, rather than flexible, is how a framework can be both extremely fast and a joy to build on.
| Principle | What it means |
|---|---|
| Speed | Introspection happens once, at startup. The request path stays minimal and predictable. |
| Opinionated DX | One way to do each thing. Contracts fail loud at startup with a precise WiringError, never quietly at runtime. |
| Strict typing | Types are the contract, the validation source, and the OpenAPI source — and the public interface is checked by every major type checker. |
jero leans hard into modern Python typing: PEP 695
generics (JSONResponse[Body, Headers], NDJSONStreamingResponse[Movie]), bounded
type parameters with defaults, and Protocols, so a handler's signature is its
schema. If you don't like typing, this isn't your framework.
For the reasoning behind those choices, read Philosophy. For a feature-by-feature contrast with other Python frameworks, read Comparison.
Highlights¶
- Startup validation — invalid apps can't boot: every contract is checked at wiring
with a precise
WiringError. → - Typed responses and headers —
JSONResponse[Body, Headers]keeps both schemas; unions of responses document every status. → - Typed streaming — NDJSON, SSE, and raw bytes, with lifecycle teardown and disconnect handling done for you. →
- Typed WebSockets — compiled framing in both directions and bounded local fan-out
through
Channel. → - Auth checked at startup — the
usertype is verified against the authenticator before a single request is served. → - Reverse routing —
Location/Linkheaders built from the route class, validated at construction. → - Compiled middleware & CORS — fixed per-tier costs, zero on uncovered routes. →
- OpenAPI from your types — a 3.1 spec plus Scalar docs UI, no duplicate schema definitions. →
- In-process
TestClient— sync, no network, full lifespan, streaming and WebSockets. →
API reference¶
The full public surface — BaseApp, BaseFactory, Resource, Endpoint, the
response and streaming types, and the test helpers — is documented in the
API reference.