BLOG
Typed, Named Endpoints for Cro (with HTMX Helpers)
Croâs HTTP router is great at declaring routes, but it doesnât provide a firstâclass way to reference those routes elsewhere in your app. Cro::HTTP::RouterUtils fills that gap: it lets you reference endpoints by name, build typed-safe paths, generate HTMX attributes, redirect to routes, and even call the underlying implementation.
Stable references to routes by name (or autoânamed fallback)
Typed
path()builder validates parameter typeshx-attrs()renders HTMX attributes with the correct method and URLredirect-to()returns a Cro redirect to the endpointcall()invokes the route implementation directly (handy for tests)Supports
includewith prefixes seamlessly
Repo: https://github.com/FCO/Cro-HTTP-RouterUtils
Install
zef install --depsonly .Quick Start
use Cro::HTTP::RouterUtils;
my $app = route {
# Name a route via a named sub
get my sub greet-path('greet', $name) {
content 'text/plain', "Hello, $name!"
}
# Use the endpoint by name
get -> 'links' {
my $ep = endpoints('greet-path');
content 'text/html', qq:to/END/
<a href="{ $ep.path(:name<alice>) }">alice</a>
<a href="#" { $ep.hx-attrs(:name<bob>, :trigger<click>) }>bob</a>
END
}
}Naming and Discovering Endpoints
Named endpoints: give your route a function name and reference it with
endpoints('your-name').Autoânamed endpoints: when no name is provided, keys are generated from method and path signature, e.g.
get_greet.
# Auto-named
get -> 'greet', Str :$name { 200 }
endpoints('get_greet').path; # => "/greet"
# Named
get my sub greet-path('greet', $name) { "Hello, $name!" }
endpoints('greet-path').path(:name<alice>); # => "/greet/alice"Includes with prefixes are supported transparently:
include external => other-routes; # /external prefix applied
endpoints('external-ep1').method; # "GET"
endpoints('external-ep1').path; # "/external/returns-ok"Typed Path Building
path(*%values) enforces your routeâs typed parameters; missing or invalid values throw.
get my sub sum('sum', Int $a, Int $b) { $a + $b }
my $ep = endpoints('sum');
$ep.path(:a(1), :b(2)); # "/sum/1/2"
$ep.path(:a("x"), :b(2)); # throws (type mismatch)
$ep.path(:a(1)); # throws (missing parameter)HTMX Helpers
hx-attrs(:argsâŠ) returns a space-separated string of HTMX attributes. It uses the endpointâs HTTP method by default (e.g., hx-get) and the built URL.
<a href="#"
{ endpoints('greet-path').hx-attrs(
:name<alice>,
:trigger<click>,
:target<#out>,
:swap<'outerHTML settle:200ms'>,
:push-url<true>,
:on{ click => "console.log(\"clicked\")" }
)
}>
Load Alice
</a>Highlights supported:
Request URL/method:
methodoverride; parameters via:name<...>etc.Core:
trigger,target,confirm,indicator,swap,oob(ashx-swap-oob),boostNavigation:
push-url(Bool|Str),replace-url(Bool|Str)Selection:
select,select-oobJSON:
vals,headers,requestFlags:
disable,validateMisc:
disabled-elt,disinherit,encoding,ext,history,history-elt,include,inherit,params,prompt,sync,vars(deprecated)Events:
:on{ event => "code" }emitshx-on:event='code'
Example minimal output:
hx-get='/greet/alice' hx-trigger='click' hx-target='#out'Redirects
get -> 'redir' {
endpoints('greet-path').redirect-to: :name<ok>
}Calling the Implementation
call(|args) invokes the underlying route implementation. Literal path segments are auto-injected; you pass only the non-literal parameters.
get my sub ret('ret') { 42 }
get my sub sum('sum', Int $a, Int $b) { $a + $b }
endpoints('ret').call; # 42
endpoints('sum').call(2, 3); # 5Great for unit tests of pure route logic. If you depend on Croâs pipeline, prefer Cro::HTTP::Test.
Full Example
See examples/example.raku and examples/ExampleRoute.rakumod in the repo. Run:
raku examples/example.rakuThen visit:
/formfor a classic form/linksfor<a href>links built from endpoints/links-htmxfor HTMX-driven links
Errors and Guarantees
Unknown endpoint name: throws.
Missing/invalid path params: throws with a clear message.
call()auto-injects literal path segments; you provide the rest.
Why This Isnât in Cro
Cro focuses on routing and request handling. This utility adds âendpoint as a valueâ ergonomicsâstable references, typed path building, HTMX helpers, and redirect/call helpersâwhile staying a thin layer on top of Cro::HTTP::Router.
Appendix: Include With Prefix Example
# examples/ExampleRoute.rakumod
use Cro::HTTP::RouterUtils;
sub other-routes is export {
route {
get my sub external-ep1("returns-ok") { content "text/plain", "OK" }
post my sub external-ep2("using-post") { content "text/plain", "OK" }
}
}
# elsewhere
include external => other-routes;
endpoints('external-ep1').path; # "/external/returns-ok"
endpoints('external-ep2').path; # "/external/using-post"â Made with Cro::HTTP::RouterUtils (Raku).