No validation layer in the framework
Stario sits on HTTP and asyncio: Request exposes query bytes and await req.body() for the entity; Writer sends the response. Everything else—how you interpret JSON, which codec you use, whether you validate with Pydantic, msgspec, attrs/cattrs, or hand-rolled checks—is a question the framework could try to answer for you. We prefer not to. This page is why: the tradeoffs are yours, and your app’s constraints beat any default we could ship.
Framework boundary
Stario gives you Request, Writer, and Datastar helpers (read_signals, patch_signals, …). It does not ship a validator—bring Pydantic, msgspec, cattrs, or manual checks at the boundary.
The questions a framework could answer
If Stario “owned” validation, we would have to decide, among other things:
Which library? — Pydantic is ubiquitous; msgspec is fast; attrs plus cattrs is flexible; rolling your own is valid for tiny payloads. Picking one bakes opinion and version coupling into the core.
Which JSON stack? — The standard library is always there but not the fastest. orjson, msgspec’s decoder, and Pydantic’s JSON mode all behave slightly differently. Shipping one in the framework means users learn our wiring, not only their library.
How do signals map to Python? — Datastar’s wire format is one JSON object per action; GET uses the
datastarquery parameter; other methods use the body. Aliases, nested models, and file-shaped values each want different rules—those rules belong in your schema, not in a generic adapter.Errors — map
ValidationErrorto 400 or 422? Include field paths in the body? That is product and API design, not framework law.
Answering all of that inside Stario would narrow the framework, raise cognitive load (“use Stario’s Pydantic integration”), and age poorly as libraries and best practices move.
What we do instead
We expose the raw edges and document patterns: Reading and writing Datastar signals shows read_signals, typed reads, and patch_signals. We link to Pydantic, msgspec, attrs, and cattrs so you pick what fits.
The strategy is to trust your judgment: you know whether you need strict schemas, soft coercion, or a prototype that json.loads into a dict. Stario stays thin so you are not fighting hidden defaults—only HTTP, Datastar helpers, and your own glue.
The “many valid shapes” idea applies to validation and codecs only—the rest of the docs may still talk about a broader “Stario way” for routing, HTML, and telemetry.
For example, consider Pydantic
The next section is optional detail for teams already using Pydantic. If you prefer msgspec, cattrs, or manual checks, skip to Many valid shapes or the signals how-to.
Pydantic alone opens a large design space—before you even choose field types.
Reading JSON: do you call BaseModel.model_validate_json on a concrete class, or TypeAdapter(SomeUnionOrTypedDict).validate_json? The second covers TypedDict, @dataclass, unions, and containers; the first is the straight line for a single BaseModel. TypeAdapter is not free to build on every request, so you end up thinking about module-level adapters, a registry keyed by message type, or bootstrap-time wiring—all of that is your deployment shape, not ours.
Writing JSON: model_dump_json returns a str; patch_signals also accepts bytes. Do you encode("utf-8"), or use TypeAdapter(...).dump_json(...) so you emit bytes in one step? Again, adapter lifetime and where you cache it are app decisions.
Names on the wire: client JSON might be camelCase while Python prefers snake_case. Do you use Field(alias=...), model config, a validation_alias, or rename at the boundary? Whether to normalize, how strict to be, and what to do when the client sends an unexpected key are policy—not something Stario can pick without guessing your API contract.
There are too many of these forks to encode in the framework without pretending we know your product. We leave you with Pydantic (or msgspec, or cattrs) as you already use it, because you know what you need: performance targets, team conventions, and how much validation noise you want in handlers versus in shared helpers.
The how-to suggests TypeAdapter for Datastar signals as one coherent pattern; it is a recipe, not a mandate.
Many valid shapes
There is no single “Stario way” to validate: msgspec structs, cattrs + attrs, Pydantic models or adapters, or json.loads into dict and manual checks are all legitimate. The fixed surface is the same as in Framework boundary above—your glue picks the codec and rules.
Recipes: Reading and writing Datastar signals · Reference: Datastar · Request