Data and state
Route data is loaded on the server and rendered into ASX. Local state stays route/component scoped, while actions return typed patches or refresh instructions through the Axonyx bridge.
Open thelive data round-trip labto send a real request and inspect browser, server, and transport timing.
Typed route data
Keep database access in a route loader or shared backend function, then call it from the page. The loader return contract supplies the page value type without making authors repeat it.
export type Post {
title: String
slug: String
summary?: String
}
query loadPosts(status: String = "published") -> Post[] {
data posts = db.posts
.where({ status: input.status })
.order({ created_at: "desc" })
.limit(6)
.all()
return posts
}page Posts() {
data posts = loadPosts()
return ASX {
<Each items={posts} as="post">
<Card title={post.title}>
<Copy>{post?.summary}</Copy>
</Card>
</Each>
}
}The route-local loader wins and gives `posts` the inferred `List<Post>` type. A unique shared query can provide the same contract; duplicate shared names fail with `axonyx-query-ambiguous` rather than binding to an arbitrary function.
Local state
A state declaration is closer to a typed reactive value than a global store. Axonyx emits bridge metadata only for pages that bind or patch that value.
Interactive State V1
This counter runs through the Rust/WASM state executor and compiler-owned event metadata. It does not reload the route or use a VDOM.
page Counter() {
state count: Number = 0
state open: Bool = false
state message: String = "Axonyx state" persist session("counter-message")
return ASX {
<>
<Button on:click={count -= 1}>Decrease</Button>
<strong>{count}</strong>
<Button on:click={count += 1}>Increase</Button>
<Button on:click={open = !open}>Toggle details</Button>
<input bind:value={message} on:input={message = event.value} />
<strong>{message}</strong>
<If when={count > 5}>
<Badge>High value</Badge>
<Else><Badge>Low range</Badge></Else>
</If>
</>
}
}V1 executes String assignment, numeric increment/decrement, Bool toggle, typed literal assignment, `event.value`, and `event.checked`. The `ax-state-event/1` envelope rejects invalid protocols, event/source mismatches, wrong types, non-finite numbers, and oversized String payloads before execution.
Add `persist local("key")` or `persist session("key")` only to state that should survive navigation or reload. The host exposes a typed storage capability, not arbitrary browser storage access; persisted browser values are still untrusted and never replace server-side authorization or validation.
Actions and patches
Actions validate browser input on the server. A successful action can patch visible state, invalidate route data, redirect, or return a typed response; the runtime selects the necessary transport.
action SetTheme(theme: String) {
require input.theme in themes else error "Unknown theme."
patch App.theme = input.theme
return ok()
}Database boundary
SQLite execution and schema checks are available today. Database secrets stay in environment variables declared by the backend contract; raw SQL remains an escape hatch through `db.query`.
cargo ax db check
cargo ax db pull