Routing
Routing has two halves: route data in routes/ and registration in handlers/. Dispatch is ordinary net/http over http.ServeMux, through pkg/anduin.
Declare a route
A route is a package-level value built with pkg/routing:
1package routes
2
3import "github.com/mbvlabs/andurel/pkg/routing"
4
5const ProductPrefix = "/products"
6
7var (
8 ProductIndex = routing.NewSimpleRoute(
9 "",
10 "products.index",
11 ProductPrefix,
12 routing.InertiaRoute(),
13 )
14 ProductShow = routing.NewRouteWithUUIDID(
15 "/:id",
16 "products.show",
17 ProductPrefix,
18 routing.InertiaRoute(),
19 )
20)
The first argument is the path relative to the prefix (start it with /; a missing or doubled slash is tolerated but logs a warning), the second is the route name, and the third is the prefix. A route value exposes:
Path()for registration and server-side linksName()as the stable route identifierURL(...)for a relative URL andFullURL(hosts, ...)for an absolute oneHost()andIsInertia()
Pick the constructor that matches the parameter:
| Constructor | Parameter |
|---|---|
NewSimpleRoute |
none |
NewRouteWithSlug |
string slug |
NewRouteWithStringID |
string ID |
NewRouteWithSerialID |
int32 ID |
NewRouteWithBigSerialID |
int64 ID |
NewRouteWithUUIDID |
uuid.UUID |
NewRouteWithToken |
string token |
NewRouteWithFile |
file name |
NewRouteWithParams |
a custom params struct |
Mark a page route with routing.InertiaRoute(). The option is what includes the route in the generated TypeScript helpers; leave it off for JSON, asset and callback endpoints.
Register a handler
1func (h ProductHandler) Register(rt *anduin.Router) {
2 rt.Get(routes.ProductIndex.Path(), h.Index)
3 rt.Get(routes.ProductShow.Path(), h.Show)
4 rt.Post(routes.ProductIndex.Path(), h.Create)
5}
Router has Get, Post, Put, Patch and Delete. Global middleware is installed once with rt.Use(...) in internal/runtime; per-route middleware follows the handler:
1rt.Post(routes.ProductIndex.Path(), h.Create, anduin.RateLimit(store, 5, 10*time.Minute, h.onLimited))
Paths use :param for a segment and a trailing * for a remainder. The router converts them to http.ServeMux patterns (:id becomes {id}, * becomes {path...}). Remember that a plain / pattern is a prefix match in net/http: register /{$} when you mean only the root. The scaffold's home handler does this so unmatched GETs reach the 404 page.
Build URLs
Use the route value instead of writing the path twice:
1routes.ProductIndex.URL() // /products
2routes.ProductShow.URL(product.ID) // /products/<uuid>
3routes.ProductShow.FullURL(hosts, product.ID) // https://example.com/products/<uuid>
4routes.ProductIndex.URL(routing.QueryParam("page", "2")) // /products?page=2
Handlers redirect with the same value:
1return h.renderer.Redirect(ctx, routes.ProductIndex.URL(), http.StatusSeeOther)
Frontend helpers
andurel sync routes reads routes/*.go as the source of truth and writes resources/js/routes.ts:
1andurel sync routes --json
1import { routes } from '@/routes'
2
3// Absolute URL once configureRouteHosts has run at boot.
4const indexURL = routes.productIndex()
5
6// Relative path, when the browser origin is already correct.
7const showPath = routes.productShow.path(product.id)
The generated helper names are the lower-camel form of the Go variable names, so keep names stable when you do not want frontend imports to change. Run the sync after adding or renaming an Inertia route.
Named hosts
A single-host application uses HOST_PRIMARY and needs nothing else. To serve a second hostname from the same process:
-
Declare a host name in
config/hosts.go:1const HostAdmin routing.HostName = "admin" -
Load
HOST_ADMINintocfg.App.Hostsinconfig.NewApp. -
Bind the route to the host:
1var AdminDashboard = routing.NewSimpleRoute( 2 "/admin", 3 "admin.dashboard", 4 "", 5 routing.Host(config.HostAdmin), 6 routing.InertiaRoute(), 7) -
Register it on that host in the handler or in
internal/runtime:1rt.Host(config.HostAdmin).Get(routes.AdminDashboard.Path(), h.Dashboard)
Unknown hosts fall back to the primary mux. Build the registry once with routing.NewHosts(cfg.App.Hosts), pass it to anduin.WithHosts, and pass the same value to FullURL. Asset URLs keep pointing at the primary host unless a route sets routing.Host(...).
Inspect routes
1andurel inspect routes --json
2andurel inspect handlers --json
inspect routes lists each route's variable, name, path, constructor, parameters, host and source file. inspect handlers lists each handler's constructor parameters and the routes its Register method mounts. Neither command writes files; neither starts the server.