← Blog
Release0.7Routing

Crown 0.7: Typed Query, Action, and serverFn

Crown 0.7 は、Next.js ライクなファイルシステムルーティングを保ったまま、 TanStack Router の型付き Search ParamsRemix の loader / actionTanStack Start の serverFn を Nim の AST マクロへ移植したリリースです。

TypeScript 側では Conditional Types や Zod で戦っている領域を、 Nim ではコンパイル時にコード生成してゼロコストにできます。 ランタイムの node_modules も、 重いクライアントバンドルもありません。出力はこれまで通り単一のネイティブバイナリです。

Typed Search Params

URL クエリは多くのフレームワークで型が弱いところです。 Crown では query マクロが オブジェクト・デフォルト値・インライン enum・Option[T] をコンパイル時に組み立てます。

query ProductsQuery: page: int = 1 sort: choice(asc, desc) = desc filter: Option[string] proc page*(req: Request): string = let q = req.search(ProductsQuery) # q.page は int。抽出と変換はマクロが生成

Loader と Action

独自のクライアント状態管理は増やしません。 loader は GET(画面に必要なデータをサーバーで一括取得)、 action は同じパスの POST(更新)です。

actionvoid / revalidate() / 空レスポンスを返すと、 Crown は同じルートの loader と page を再実行して最新 HTML を返します。 リダイレクトや部分 HTML を返せば、revalidate は走りません。 crown-post にターゲットが無いフォームは、 フルドキュメントを差し替えます。

proc loader*(req: Request): seq[Product] = getProducts(req.search(ProductsQuery)) proc action*(req: Request) = db.insert(req.form(AddProduct)) # 成功後に loader → page が自動実行される proc page*(req: Request, data: seq[Product]): string = render(data)

Typed Links

href="/users/" & id のような文字列結合はリンク切れの元です。 ルートマニフェストから hrefUsers(id: int) を生成し、 クエリ付き URL は href("/products", q) で組み立てます。 タイポと型の不一致はコンパイルエラーになります。

serverFn

TanStack Start の createServerFn 相当です。 マクロはサーバー上の実体 proc と JSON インボーカを同時に生成し、 POST /__crown/fn/<name> として公開します。

serverFn: proc updateUser*(id: string, name: string): User = db.users.update(id, name) # window.crownServerFn("updateUser", {id: "1", name: "Ada"})

これから

今回の焦点は「型」と「HTTP サイクル」です。 Vinxi / Nitro 思想の Edge・WASM・SSG マルチターゲットは、 Nim の公式バックエンド(C / JS / WASM)に沿って今後伸ばします。 いまの本番経路は、これまで通り crown build の単一ネイティブバイナリです。

詳細は Typed Search / LinksLoader & ActionServer Functions を参照してください。サンプルアプリの /products でも同じパターンを試せます。