Routing and APIs

Keep route handlers thin, use file-based routing conventions, and understand navigation, layouts, page actions, and API endpoints.

#routes#params#api#navigation

Routing and APIs

Route code in Vango is intentionally thin. Page handlers select views. Stateful logic belongs in components.

File-based routing

Vango scans app/routes and maps files to routes.

Examples:

  • app/routes/index.go -> /

  • app/routes/about.go -> /about

  • app/routes/projects/index.go -> /projects

  • app/routes/projects/id_/index.go -> /projects/:id

  • app/routes/docs/path___/index.go -> /docs/*path

Use parameter directories like id_ and path___ for Go-friendly imports.

Page handler signatures

Supported shapes:

go
1func Page(ctx vango.Ctx) *vango.VNode
2func Page(ctx vango.Ctx, p Params) *vango.VNode

Rules:

  • parse params

  • choose the page component

  • return the view

Do not put blocking I/O, signal allocation, or imperative navigation in the page handler.

Params

Use typed params with struct tags.

go
1type Params struct {
2	ID int `param:"id"`
3}

Use route params for IDs, slugs, UUIDs, and catch-all segments. Use URL params for filters, sorting, and shareable view state.

Layouts

Layouts are pure view wrappers.

go
1func Layout(ctx vango.Ctx, children vango.Slot) *vango.VNode

Good layout responsibilities:

  • shell structure

  • navigation

  • asset tags

  • persistent banners

  • runtime script injection

If a layout needs state, embed a Setup component within it.

Prefer declarative navigation for normal user flows.

go
1Link("/projects", Text("Projects"))
2NavLink(ctx, "/settings", Text("Settings"))

Use ctx.Navigate(...) from event handlers, OnMount, or Effect when navigation is driven by state.

Do not call ctx.Navigate(...) from a page handler or render closure.

That is one of the most common Vango correctness failures.

Progressive page actions

For forms that submit back to the current page route, pair setup.RouteForm with a page Action.

File-based route files may export:

go
1func Page(ctx vango.Ctx) *vango.VNode
2func Action(ctx vango.ActionCtx, in Input) (*vango.FormResult, error)

Rules:

  • page actions are route-bound POST handlers

  • input type should be a struct

  • use ctx.StdContext() for service and DB calls

  • business-rule validation returns vango.Invalid(...)

  • success redirects return vango.RedirectTo(...)

API routes

API files live under app/routes/api.

Typical signatures:

go
1func GET(ctx vango.Ctx) (T, error)
2func GET(ctx vango.Ctx, p Params) (T, error)
3func POST(ctx vango.Ctx, in Body) (T, error)
4func POST(ctx vango.Ctx, p Params, in Body) (T, error)

Return plain typed values when you do not need explicit status metadata. Return *vango.Response[T] when you do.

Useful helpers:

  • vango.OK(...)

  • vango.Created(...)

  • vango.Accepted(...)

  • vango.NoContent[T]()

  • vango.Paginated(...)

Useful HTTP error helpers:

  • vango.BadRequest(...)

  • vango.Unauthorized(...)

  • vango.Forbidden(...)

  • vango.NotFound(...)

  • vango.Conflict(...)

  • vango.UnprocessableEntity(...)

  • vango.InternalError(...)

Email integrations and custom HTTP boundaries

Treat email as service-layer I/O. Do not send it from render logic or session-loop lifecycle callbacks.

Provider webhooks are plain HTTP ingress, not Vango route handlers. Mount them on the HTTP boundary and continue routing ordinary app traffic through the Vango server handler.

The next major layer is form handling and auth. Continue to Forms and Auth.