Help us shape the future of Go! Take the 2026 Go Developer Survey

router

package
v0.34.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package router is a thin layer over net/http.ServeMux (Go 1.22+ patterns) adding route groups, named routes with URL generation, resource controllers and three middleware stacks:

  • server middleware runs on every request, even when no route matches;
  • router middleware runs only when a route matches;
  • route/group middleware runs for the routes it is attached to.

Middleware has the standard signature func(http.Handler) http.Handler, so any net/http middleware works.

New returns a Router; Get, Post, Group and Resource register Route values whose handlers are web.HandlerFunc, and Router.Handler compiles them into an http.Handler. Apps receive the router in vukem.WithRoutes and reach named routes through Router.URL or the "route" template function.

See https://github.com/AlexGalhardo/vukem/blob/main/docs/http.md.

Example
package main

import (
	"fmt"
	"log/slog"
	"net/http"
	"net/http/httptest"

	"github.com/AlexGalhardo/vukem/router"
	"github.com/AlexGalhardo/vukem/web"
)

func main() {
	r := router.New(&web.Services{Log: slog.New(slog.DiscardHandler)})
	r.Get("/posts/{id}", func(c *web.Ctx) error {
		return c.String(http.StatusOK, "post "+c.Param("id"))
	}).Name("posts.show")

	h, err := r.Handler()
	if err != nil {
		fmt.Println(err)
		return
	}

	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/posts/42", http.NoBody))
	fmt.Println(rec.Code, rec.Body.String())

	link, err := r.URL("posts.show", "id", 7)
	fmt.Println(link, err)
}
Output:
200 post 42
/posts/7 <nil>

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Adapt

func Adapt(h web.HandlerFunc, services *web.Services) http.Handler

Adapt converts a HandlerFunc into an http.Handler using services.

func Wrap

func Wrap(h http.Handler) web.HandlerFunc

Wrap converts an http.Handler into a HandlerFunc.

Types

type Creator

type Creator interface{ Create(c *web.Ctx) error }

Creator handles GET /posts/create (HTML form).

type Destroyer

type Destroyer interface{ Destroy(c *web.Ctx) error }

Destroyer handles DELETE /posts/{post}.

type Editor

type Editor interface{ Edit(c *web.Ctx) error }

Editor handles GET /posts/{post}/edit (HTML form).

type Group

type Group struct {
	// contains filtered or unexported fields
}

Group is a set of routes sharing a path prefix, a name prefix and middleware. Groups nest.

func (*Group) APIResource

func (g *Group) APIResource(name string, ctrl any) *Resource

APIResource is Resource without the create and edit form routes.

func (*Group) Any

func (g *Group) Any(path string, h web.HandlerFunc) *Route

Any registers path for every method.

func (*Group) Delete

func (g *Group) Delete(path string, h web.HandlerFunc) *Route

Delete registers a DELETE route.

func (*Group) ErrorHandler added in v0.34.0

func (g *Group) ErrorHandler(wrap func(next web.ErrorHandler) web.ErrorHandler) *Group

ErrorHandler sets how errors are rendered for the group's routes, nested groups included, whether a handler returned them or a middleware failed through web.Fail or web.ServicesFrom. wrap receives the handler that would run otherwise (an outer group's, the router's Services.Errors, or web.DefaultErrorHandler) and returns its replacement, so it may decorate the response and delegate:

api.ErrorHandler(func(next web.ErrorHandler) web.ErrorHandler {
	return func(c *web.Ctx, err error) {
		c.Writer().Header().Set("Cache-Control", "no-store")
		next(c, err)
	}
})

wrap runs on every request of the group, so keep it cheap. A later call replaces an earlier one; 404 and 405 answers for unmatched paths keep the router's handler.

func (*Group) Get

func (g *Group) Get(path string, h web.HandlerFunc) *Route

Get registers a GET route (it also answers HEAD).

func (*Group) Group

func (g *Group) Group(prefix string, fn func(g *Group)) *Group

Group creates a nested group (see Router.Group for "/" and "").

func (*Group) Handle

func (g *Group) Handle(pattern string, h http.Handler) *Route

Handle mounts an http.Handler; pattern may start with a method.

func (*Group) Match

func (g *Group) Match(methods []string, path string, h web.HandlerFunc) []*Route

Match registers path for each method.

func (*Group) Name

func (g *Group) Name(prefix string) *Group

Name sets the name prefix of the group ("admin." makes "admin.posts.index").

func (*Group) Options

func (g *Group) Options(path string, h web.HandlerFunc) *Route

Options registers an OPTIONS route.

func (*Group) Patch

func (g *Group) Patch(path string, h web.HandlerFunc) *Route

Patch registers a PATCH route.

func (*Group) Post

func (g *Group) Post(path string, h web.HandlerFunc) *Route

Post registers a POST route.

func (*Group) Put

func (g *Group) Put(path string, h web.HandlerFunc) *Route

Put registers a PUT route.

func (*Group) Resource

func (g *Group) Resource(name string, ctrl any) *Resource

Resource registers RESTful routes for ctrl under /name. Nested resources use dots: "posts.comments" → /posts/{post}/comments/{comment}. Route names are "<name>.<action>".

func (*Group) Use

func (g *Group) Use(mw ...Middleware) *Group

Use appends middleware applied to every route of the group (including routes registered before the call and in nested groups).

type Indexer

type Indexer interface{ Index(c *web.Ctx) error }

Indexer handles GET /posts.

type Middleware

type Middleware = func(http.Handler) http.Handler

Middleware wraps an http.Handler.

type Resource

type Resource struct {
	// contains filtered or unexported fields
}

Resource is a set of routes generated from a controller.

func (*Resource) Except

func (rs *Resource) Except(actions ...string) *Resource

Except removes the named actions.

func (*Resource) Only

func (rs *Resource) Only(actions ...string) *Resource

Only keeps the named actions and removes the others.

func (*Resource) Shallow

func (rs *Resource) Shallow() *Resource

Shallow makes the member routes of a nested resource top-level, as their IDs are unique on their own: /posts/{post}/comments stays for index/create/store, while show/edit/update/destroy become /comments/{comment}. Route names do not change.

func (*Resource) Use

func (rs *Resource) Use(mw ...Middleware) *Resource

Use applies middleware to the resource routes (all actions when none are given by Only/Except before).

type Route

type Route struct {
	// contains filtered or unexported fields
}

Route is a registered route.

func (*Route) Doc added in v0.15.0

func (rt *Route) Doc(v any) *Route

Doc attaches documentation (e.g. an api.Operation) that generators such as the OpenAPI exporter read back through RouteInfo.Doc.

func (*Route) FullName

func (rt *Route) FullName() string

FullName returns the name including group prefixes, or "".

func (*Route) Host added in v0.33.0

func (rt *Route) Host() string

Host returns the host the route is restricted to (see Router.Host), or "".

func (*Route) Name

func (rt *Route) Name(name string) *Route

Name sets the route name used by URL generation. The names of enclosing groups are prefixed when the router is compiled.

func (*Route) Path added in v0.15.0

func (rt *Route) Path() string

Path returns the full pattern path, group prefixes included.

func (*Route) Use

func (rt *Route) Use(mw ...Middleware) *Route

Use appends route middleware.

type RouteInfo

type RouteInfo struct {
	Method string `json:"method"`
	// Host is the host the route is restricted to (Router.Host), or "".
	Host string `json:"host,omitempty"`
	Path string `json:"path"`
	Name string `json:"name,omitempty"`
	// LocalName is the name given to the route itself, without the group
	// name prefixes (Name is the full name).
	LocalName  string   `json:"-"`
	Middleware []string `json:"middleware,omitempty"`
	Doc        any      `json:"-"`
}

RouteInfo describes a compiled route.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router collects routes and compiles them into an http.Handler.

func New

func New(services *web.Services) *Router

New returns an empty Router. services may be nil.

func (*Router) APIResource

func (r *Router) APIResource(name string, ctrl any) *Resource

APIResource is Resource without the create and edit form routes.

func (*Router) Any

func (r *Router) Any(path string, h web.HandlerFunc) *Route

Any registers a route for every method.

func (*Router) Compiled added in v0.25.0

func (r *Router) Compiled() bool

Compiled reports whether the routes were compiled (by Handler, URL or a first request). Routes added after that are not served.

func (*Router) Delete

func (r *Router) Delete(path string, h web.HandlerFunc) *Route

Delete registers a DELETE route.

func (*Router) Get

func (r *Router) Get(path string, h web.HandlerFunc) *Route

Get registers a GET route (it also answers HEAD).

func (*Router) Group

func (r *Router) Group(prefix string, fn func(g *Group)) *Group

Group creates a route group under prefix. Inside it, g.Get("/", h) serves prefix+"/" exactly (ServeMux redirects the bare prefix there) and g.Get("", h) serves the bare prefix; neither is a catch-all.

Example
package main

import (
	"fmt"
	"log/slog"
	"net/http"

	"github.com/AlexGalhardo/vukem/router"
	"github.com/AlexGalhardo/vukem/web"
)

func main() {
	r := router.New(&web.Services{Log: slog.New(slog.DiscardHandler)})
	r.Group("/admin", func(g *router.Group) {
		g.Get("/dashboard", func(c *web.Ctx) error {
			return c.String(http.StatusOK, "dashboard")
		}).Name("dashboard")
	}).Name("admin.")

	for _, route := range r.Routes() {
		fmt.Println(route.Method, route.Path, route.Name)
	}
}
Output:
GET /admin/dashboard admin.dashboard

func (*Router) Handle

func (r *Router) Handle(pattern string, h http.Handler) *Route

Handle mounts a plain http.Handler. pattern uses ServeMux syntax and may start with a method ("GET /metrics").

func (*Router) Handler

func (r *Router) Handler() (http.Handler, error)

Handler compiles the routes. It reports pattern conflicts, invalid patterns and duplicate names. Routes added afterwards are ignored.

func (*Router) Host added in v0.33.0

func (r *Router) Host(host string, fn func(g *Group)) *Group

Host creates a group whose routes match only requests for host (any port), e.g. r.Host("api.example.com", func(g *router.Group) {...}) next to the same paths on the main host. Nested groups keep the host. host is a lowercase name without port, path or wildcard; anything else is a Handler error. URL returns the scheme-relative "//host/path" for these routes, so links work from any host.

func (*Router) Match

func (r *Router) Match(methods []string, path string, h web.HandlerFunc) []*Route

Match registers a route for several methods.

func (*Router) Options

func (r *Router) Options(path string, h web.HandlerFunc) *Route

Options registers an OPTIONS route.

func (*Router) Patch

func (r *Router) Patch(path string, h web.HandlerFunc) *Route

Patch registers a PATCH route.

func (*Router) Post

func (r *Router) Post(path string, h web.HandlerFunc) *Route

Post registers a POST route.

func (*Router) Put

func (r *Router) Put(path string, h web.HandlerFunc) *Route

Put registers a PUT route.

func (*Router) Resource

func (r *Router) Resource(name string, ctrl any) *Resource

Resource registers the actions ctrl implements (see Indexer, Storer, ...).

func (*Router) Routes

func (r *Router) Routes() []RouteInfo

Routes lists compiled routes in registration order.

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP compiles on first use and serves; it panics on a build error, so call Handler at boot to surface errors early.

func (*Router) Services

func (r *Router) Services() *web.Services

Services returns the shared services passed to every *web.Ctx.

func (*Router) URL

func (r *Router) URL(name string, params ...any) (string, error)

URL implements web.URLGenerator: params are name/value pairs filling the route's {wildcards}. Missing or unknown params are errors.

func (*Router) Use

func (r *Router) Use(mw ...Middleware)

Use appends middleware that runs for every matched route.

func (*Router) UseServer

func (r *Router) UseServer(mw ...Middleware)

UseServer appends middleware that runs for every request.

type Shower

type Shower interface{ Show(c *web.Ctx) error }

Shower handles GET /posts/{post}.

type Storer

type Storer interface{ Store(c *web.Ctx) error }

Storer handles POST /posts.

type Updater

type Updater interface{ Update(c *web.Ctx) error }

Updater handles PUT and PATCH /posts/{post}.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL