router

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 17 Imported by: 0

README

router

CI CodeQL Coverage Mutation Documentation Go Reference Release Go License

router is an explicit, immutable HTTP router built on Go's net/http programming model. It adds deterministic composition, groups, names, safe URL generation, metadata, introspection, mounts, and route-scoped middleware while keeping handlers as ordinary http.Handler values.

The minimum supported toolchain is Go 1.26.6. The package has no runtime dependencies and no global router, reflection discovery, controller resolver, container, session, template, or application lifecycle.

Five-minute start

builder := router.New()
err := builder.Register(router.Route{
    Name:    "users.show",
    Methods: []string{http.MethodGet},
    Path:    "/users/{id}",
    Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, r.PathValue("id"))
    }),
})
if err != nil {
    return err
}

handler, err := builder.Compile()
if err != nil {
    return err
}
return http.ListenAndServe(":8080", handler)

Registration is single-owner and startup-time. The compiled router is an immutable http.Handler safe for concurrent serving and introspection.

Contracts

Development

Run make check for the blocking local checks and make check-all to include advisory NilAway. Each target is independently reproducible.

License

MIT. See LICENSE and NOTICE.

Ecosystem

Use the Golib documentation portal to choose companion packages, supported stacks, recipes, and operations guidance.

Documentation

Overview

Package router provides explicit, immutable HTTP route composition on top of the standard net/http programming model.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidRoute identifies a malformed route descriptor.
	ErrInvalidRoute = errors.New("invalid route")
	// ErrConflict identifies ambiguous or duplicate semantic routes.
	ErrConflict = errors.New("route conflict")
	// ErrDuplicateName identifies a repeated stable route name.
	ErrDuplicateName = errors.New("duplicate route name")
	// ErrInvalidParameter identifies malformed URL-generation parameters.
	ErrInvalidParameter = errors.New("invalid route parameter")
	// ErrGeneration identifies a named-route URL generation failure.
	ErrGeneration = errors.New("route generation failed")
	// ErrUnsupported identifies behavior deliberately unsupported by v1.
	ErrUnsupported = errors.New("unsupported routing behavior")
	// ErrCompileState identifies use of a builder after successful compilation.
	ErrCompileState = errors.New("invalid router compile state")
	// ErrLimitExceeded identifies a configured resource-budget violation.
	ErrLimitExceeded = errors.New("router limit exceeded")
)

Functions

This section is empty.

Types

type BaseURL

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

BaseURL is an immutable validated absolute-URL base.

func NewBaseURL

func NewBaseURL(scheme, authority string) (BaseURL, error)

NewBaseURL validates and trusts one explicit HTTP or HTTPS authority.

type Builder

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

Builder owns mutable startup-time registration state.

Example
package main

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

	router "github.com/faustbrian/go-router"
)

func main() {
	builder := router.New()
	_ = builder.Register(router.Route{
		Name: "users.show", Methods: []string{http.MethodGet},
		Path: "/users/{id}",
		Handler: http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
			fmt.Fprint(writer, request.PathValue("id"))
		}),
	})
	compiled, _ := builder.Compile()
	response := httptest.NewRecorder()
	compiled.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/users/42", nil))
	fmt.Println(response.Body.String())
}
Output:
42

func New

func New(options ...Option) *Builder

New creates an empty single-owner route builder.

func (*Builder) Compile

func (b *Builder) Compile() (*Router, error)

Compile validates the complete route set and returns an immutable router.

func (*Builder) Group

func (b *Builder) Group(options GroupOptions, define func(*Builder) error) error

Group transactionally flattens routes registered by define. If validation or define fails, no route from the group is published to the parent.

Example
package main

import (
	"fmt"
	"net/http"

	router "github.com/faustbrian/go-router"
)

func main() {
	builder := router.New()
	_ = builder.Group(router.GroupOptions{PathPrefix: "/api", NamePrefix: "api."}, func(group *router.Builder) error {
		return group.Register(router.Route{
			Name: "health", Methods: []string{http.MethodGet}, Path: "/health",
			Handler: http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
				writer.WriteHeader(http.StatusNoContent)
			}),
		})
	})
	compiled, _ := builder.Compile()
	fmt.Println(compiled.Routes()[0].Name, compiled.Routes()[0].Pattern)
}
Output:
api.health /api/health

func (*Builder) Mount

func (b *Builder) Mount(prefix string, handler http.Handler, options MountOptions) error

Mount registers handler below an explicit path boundary. The mount is one ordinary remainder-wildcard route and therefore follows the active redirect policy for a request missing the boundary's trailing slash.

Example
package main

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

	router "github.com/faustbrian/go-router"
)

func main() {
	builder := router.New()
	rpc := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		fmt.Fprint(writer, request.URL.Path)
	})
	_ = builder.Mount("/rpc", rpc, router.MountOptions{StripPrefix: true})
	compiled, _ := builder.Compile()
	response := httptest.NewRecorder()
	compiled.ServeHTTP(response, httptest.NewRequest(http.MethodPost, "/rpc/method", nil))
	fmt.Println(response.Body.String())
}
Output:
/method

func (*Builder) PendingRoutes

func (b *Builder) PendingRoutes() []Route

PendingRoutes returns copied descriptors registered before compilation.

func (*Builder) Register

func (b *Builder) Register(route Route) error

Register validates and copies one route descriptor.

type Error

type Error struct {
	Kind   error
	Field  string
	Source string
	Detail string
}

Error is a bounded startup or generation diagnostic. Kind supports errors.Is, while callers may use errors.As to inspect Field and Source.

func (*Error) Error

func (e *Error) Error() string

Error returns a deterministic diagnostic without request data or handlers.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap exposes the stable error category.

type GroupOptions

type GroupOptions struct {
	Host       string
	PathPrefix string
	NamePrefix string
	Middleware []NamedMiddleware
	Metadata   map[string]string
}

GroupOptions composes a host, path and name prefix, middleware, and metadata into every route registered by a group callback.

type Limits

type Limits struct {
	MaxRoutes             int
	MaxGroups             int
	MaxGroupDepth         int
	MaxMethodsPerRoute    int
	MaxMethodBytes        int
	MaxWildcardsPerRoute  int
	MaxWildcardNameBytes  int
	MaxPatternBytes       int
	MaxHostBytes          int
	MaxNameBytes          int
	MaxSourceBytes        int
	MaxOperationBytes     int
	MaxDocumentationBytes int
	MaxMetadataEntries    int
	MaxMetadataKeyBytes   int
	MaxMetadataValueBytes int
	MaxMiddleware         int
	MaxRequestTargetBytes int
	MaxURLParameters      int
	MaxURLParameterBytes  int
	MaxQueryValues        int
	MaxQueryBytes         int
	MaxGeneratedURLBytes  int
}

Limits bounds all construction and URL-generation inputs. Zero values are invalid; start with DefaultLimits and adjust individual budgets.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits returns conservative production budgets.

type Middleware

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

Middleware is the standard HTTP middleware shape.

type MountOptions

type MountOptions struct {
	Name          string
	Methods       []string
	Host          string
	Middleware    []NamedMiddleware
	Metadata      map[string]string
	Documentation string
	Operation     string
	Source        string
	StripPrefix   bool
}

MountOptions configures an explicit standard-handler mount.

type NamedMiddleware

type NamedMiddleware struct {
	Name       string
	Middleware Middleware
}

NamedMiddleware makes a middleware layer visible through introspection. Name may be empty when exclusion and duplicate detection are not needed.

type Option

type Option func(*Builder)

Option configures a Builder.

func WithAutomaticOPTIONS

func WithAutomaticOPTIONS(enabled bool) Option

WithAutomaticOPTIONS controls package-generated OPTIONS responses.

func WithLimits

func WithLimits(limits Limits) Option

WithLimits replaces all construction and generation limits.

func WithMethodNotAllowed

func WithMethodNotAllowed(handler http.Handler) Option

WithMethodNotAllowed replaces the minimal default 405 handler. The router sets Allow before invoking it.

func WithMiddleware

func WithMiddleware(middleware ...NamedMiddleware) Option

WithMiddleware sets router-wide middleware in request execution order.

func WithNotFound

func WithNotFound(handler http.Handler) Option

WithNotFound replaces the minimal default 404 handler.

func WithRedirectPolicy

func WithRedirectPolicy(policy RedirectPolicy) Option

WithRedirectPolicy selects explicit canonical-path redirect behavior.

type RedirectPolicy

type RedirectPolicy uint8

RedirectPolicy controls ServeMux canonical-path and subtree redirects.

const (
	// FollowRedirects preserves the standard ServeMux redirect behavior.
	FollowRedirects RedirectPolicy = iota
	// RejectRedirects treats a match requiring canonicalization as not found.
	RejectRedirects
)

type Route

type Route struct {
	Name              string
	Methods           []string
	Host              string
	Path              string
	Handler           http.Handler
	Middleware        []NamedMiddleware
	ExcludeMiddleware []string
	Metadata          map[string]string
	Documentation     string
	Operation         string
	Source            string
}

Route is an explicit route descriptor. Builder.Register copies every slice and map before retaining it.

type RouteInfo

type RouteInfo struct {
	Name          string
	Methods       []string
	Host          string
	Pattern       string
	Parameters    []string
	Middleware    []string
	Metadata      map[string]string
	Documentation string
	Operation     string
	Source        string
}

RouteInfo is a safe immutable view of a compiled route. Methods, Parameters, Middleware, and Metadata are copied whenever information crosses the API.

func MatchedRoute

func MatchedRoute(request *http.Request) (RouteInfo, bool)

MatchedRoute returns the route selected for request, when called from its handler or middleware chain.

type Router

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

Router is an immutable concurrency-safe compiled HTTP handler.

func (*Router) Path

func (r *Router) Path(name string, parameters ...URLParameter) (string, error)

Path generates a relative escaped path for a named route. Host wildcard values are intentionally not accepted by relative generation.

Example
package main

import (
	"fmt"
	"net/http"

	router "github.com/faustbrian/go-router"
)

func main() {
	builder := router.New()
	_ = builder.Register(router.Route{
		Name: "files.show", Methods: []string{http.MethodGet}, Path: "/files/{name}",
		Handler: http.NotFoundHandler(),
	})
	compiled, _ := builder.Compile()
	path, _ := compiled.Path("files.show", router.Param("name", "a/b.txt"))
	fmt.Println(path)
}
Output:
/files/a%2Fb.txt

func (*Router) Routes

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

Routes returns the deterministic compiled route table.

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(writer http.ResponseWriter, request *http.Request)

ServeHTTP dispatches one request without mutating compiled state.

func (*Router) URL

func (r *Router) URL(name string, base BaseURL, query url.Values, parameters ...URLParameter) (string, error)

URL generates an absolute URL using a validated explicit base. A route host replaces the base hostname while retaining its trusted explicit port.

Example
package main

import (
	"fmt"
	"net/http"
	"net/url"

	router "github.com/faustbrian/go-router"
)

func main() {
	builder := router.New()
	_ = builder.Register(router.Route{
		Name: "tenant.user", Methods: []string{http.MethodGet},
		Host: "{tenant}.example.com", Path: "/users/{id}", Handler: http.NotFoundHandler(),
	})
	compiled, _ := builder.Compile()
	base, _ := router.NewBaseURL("https", "example.com")
	generated, _ := compiled.URL(
		"tenant.user", base, url.Values{"tab": {"profile"}},
		router.Param("tenant", "acme"), router.Param("id", "42"),
	)
	fmt.Println(generated)
}
Output:
https://acme.example.com/users/42?tab=profile

type URLParameter

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

URLParameter is an explicitly typed named-route generation input. Construct values with Param or Remainder.

func Param

func Param(name, value string) URLParameter

Param supplies one path segment or host label.

func Remainder

func Remainder(name string, segments ...string) URLParameter

Remainder supplies explicit path segments for a remainder wildcard. Inputs above the package hard ceiling are rejected during generation without being copied.

Directories

Path Synopsis
Package routertest provides small consumer-facing helpers for compiled route tests without introducing a parallel runtime API.
Package routertest provides small consumer-facing helpers for compiled route tests without introducing a parallel runtime API.

Jump to

Keyboard shortcuts

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