r

package module
v0.0.0-...-28816b5 Latest Latest
Warning

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

Go to latest
Published: Feb 7, 2026 License: MIT Imports: 13 Imported by: 0

README

r - is a thin wrapper around http.ServeMux

Go Reference Go Report Card codecov License

Overview

r is a lightweight, idiomatic Go HTTP router built on top of the standard library's http.ServeMux. It provides a clean, composable API for building HTTP servers with route grouping, middleware support, and error handling—all while maintaining zero dependencies beyond the Go standard library and minimal external packages for testing and UUID generation.

Key Features
  • Route Grouping: Organize routes into logical groups with shared prefixes and middleware
  • Flexible Middleware: Apply middleware at router, group, or route level with priority-based execution
  • Pre-Middleware: Register middleware that executes before route matching
  • Error Handling: Centralized error handling with custom error handlers
  • Type-Safe: Built-in types for handlers, middleware, and error handlers
  • Method Shorthands: Convenience methods for all HTTP verbs (GET, POST, PUT, DELETE, PATCH, etc.)
  • Zero Abstraction Leak: Leverages http.ServeMux pattern matching without re-implementing routing logic
  • Minimal Dependencies: Only uses github.com/google/uuid for middleware IDs and github.com/stretchr/testify for testing
Why r?

While Go's http.ServeMux is powerful, it lacks built-in support for route grouping and middleware at the router level. r fills this gap by providing a thin, composable wrapper that:

  • Maintains the simplicity and performance of the standard library
  • Adds essential features for building production-grade HTTP services
  • Follows idiomatic Go patterns and conventions
  • Provides a clean, fluent API for route definition

Installation

go get github.com/gowool/r

Quick Start

package main

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

    "github.com/gowool/r"
)

func main() {
    router := r.NewRouter(r.ErrorHandlerFunc(func(w http.ResponseWriter, r *http.Request, err error) {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }))

    router.GET("/health", r.HandlerFunc(func(w http.ResponseWriter, req *http.Request) error {
		w.WriteHeader(http.StatusOK)
        _, _ = w.Write([]byte("OK"))
        return nil
    }))

    api := router.Group("/api")
    api.UseFunc(loggingMiddleware)

    v1 := api.Group("/v1")
    v1.GET("/users", listUsers())
    v1.POST("/users", createUser())

    handler, _ := router.Build()
    _ = http.ListenAndServe(":8080", handler)
}

func loggingMiddleware(next r.Handler) r.Handler {
    return r.HandlerFunc(func(w http.ResponseWriter, req *http.Request) error {
		defer slog.Info("request",
			slog.String("method", req.Method),
			slog.String("protocol", req.Proto),
			slog.String("host", req.Host),
			slog.String("pattern", req.Pattern),
			slog.String("uri", req.RequestURI),
			slog.String("path", req.URL.Path),
			slog.String("referer", req.Referer()),
			slog.String("user_agent", req.UserAgent()),
        )
		
        return next.ServeHTTP(w, req)
    })
}

func listUsers() r.Handler {
	return r.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) error {
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte("users"))
		return nil
	})
}

func createUser() r.Handler {
	return r.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) error {
		w.WriteHeader(http.StatusCreated)
		return nil
	})
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

View Source
const (
	IndexPage = "index.html"
)

Variables

View Source
var ErrFileNotFound = errors.New("file not found")

Functions

This section is empty.

Types

type BeforeServeFunc

type BeforeServeFunc func(http.ResponseWriter, *http.Request, fs.File, fs.FileInfo)

func Attachment

func Attachment(name string) BeforeServeFunc

Attachment set header to send a response as attachment, prompting client to save the file.

func CacheControl

func CacheControl(data ...string) BeforeServeFunc

func ContentSecurityPolicy

func ContentSecurityPolicy(data ...string) BeforeServeFunc

func Inline

func Inline(name string) BeforeServeFunc

Inline set header to send a response as inline, opening the file in the browser.

type ErrorHandler

type ErrorHandler interface {
	ServeHTTP(http.ResponseWriter, *http.Request, error)
}

type ErrorHandlerFunc

type ErrorHandlerFunc func(http.ResponseWriter, *http.Request, error)

func (ErrorHandlerFunc) ServeHTTP

func (f ErrorHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, e error)

type Handler

type Handler interface {
	ServeHTTP(http.ResponseWriter, *http.Request) error
}

func FileFS

func FileFS(fsys fs.FS, filename string, beforeServe ...BeforeServeFunc) Handler

FileFS serves the specified filename from fsys.

func StaticFS

func StaticFS(fsys fs.FS, param string, indexFallback bool, beforeServe ...BeforeServeFunc) Handler

StaticFS serve static directory content from fsys.

If a file resource is missing and indexFallback is set, the request will be forwarded to the base index.html (useful for SPA with pretty urls).

Special redirects:

  • if "path" is a file that ends in index.html, it is redirected to its non-index.html version (eg. /test/index.html -> /test/)
  • if "path" is a directory that has index.html, the index.html file is rendered, otherwise if missing - returns ErrFileNotFound or fallback to the root index.html if indexFallback is set

Example:

fsys := os.DirFS("./public")
router.GET("/files/{path...}", StaticFS(fsys, "path", false))

type HandlerFunc

type HandlerFunc func(http.ResponseWriter, *http.Request) error

func (HandlerFunc) ServeHTTP

func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) error

type Middleware

type Middleware struct {
	ID       string
	Priority int
	Func     func(Handler) Handler
}

type Middlewares

type Middlewares []*Middleware

type Route

type Route struct {
	Method      string
	Path        string
	Handler     Handler
	Middlewares Middlewares
}

func (*Route) Use

func (route *Route) Use(middlewares ...*Middleware) *Route

Use registers one or multiple middleware handlers to the current route.

func (*Route) UseFunc

func (route *Route) UseFunc(middlewareFuncs ...func(Handler) Handler) *Route

UseFunc registers one or multiple middleware functions to the current route.

The registered middleware functions are "anonymous" and with default priority, aka. executes in the order they were registered.

If you need to specify a named middleware or middleware with custom exec prirority, use the Route.Use method.

type Router

type Router struct {
	*RouterGroup

	PreMiddlewares Middlewares
	// contains filtered or unexported fields
}

func NewRouter

func NewRouter(errorHandler ErrorHandler) *Router

func (*Router) Build

func (r *Router) Build() http.Handler

func (*Router) BuildWithMux

func (r *Router) BuildWithMux(mux *http.ServeMux) http.Handler

func (*Router) Patterns

func (r *Router) Patterns() iter.Seq[string]

Patterns returns a sequence of all route patterns currently registered in the router as strings.

func (*Router) Pre

func (r *Router) Pre(middlewares ...*Middleware)

Pre registers one or multiple middleware handlers which are run before router tries to find matching route.

func (*Router) PreFunc

func (r *Router) PreFunc(middlewareFuncs ...func(Handler) Handler)

PreFunc registers one or multiple middleware functions which are run before router tries to find matching route.

The registered middleware functions are "anonymous" and with default priority, aka. executes in the order they were registered.

If you need to specify a named middleware or middleware with custom exec priority, use Router.Pre method.

func (*Router) ServeHTTP

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

ServeHTTP handles HTTP requests by initializing the router's handler and delegating the request to it.

type RouterGroup

type RouterGroup struct {
	Prefix      string
	Middlewares Middlewares
	// contains filtered or unexported fields
}

func (*RouterGroup) Any

func (group *RouterGroup) Any(path string, handler Handler) *Route

Any is a shorthand for RouterGroup.Route with "" as route method (aka. matches any method).

func (*RouterGroup) DELETE

func (group *RouterGroup) DELETE(path string, handler Handler) *Route

DELETE is a shorthand for RouterGroup.Route with DELETE as route method.

func (*RouterGroup) GET

func (group *RouterGroup) GET(path string, handler Handler) *Route

GET is a shorthand for RouterGroup.Route with GET as route method.

func (*RouterGroup) Group

func (group *RouterGroup) Group(prefix string) *RouterGroup

Group creates and register a new child RouterGroup into the current one with the specified prefix.

The prefix follows the standard Go net/http http.ServeMux pattern format ("[HOST]/[PATH]") and will be concatenated recursively into the final route path, meaning that only the root level group could have HOST as part of the prefix.

Returns the newly created group to allow chaining and registering sub-routes and group specific middlewares.

func (*RouterGroup) HEAD

func (group *RouterGroup) HEAD(path string, handler Handler) *Route

HEAD is a shorthand for RouterGroup.Route with HEAD as route method.

func (*RouterGroup) OPTIONS

func (group *RouterGroup) OPTIONS(path string, handler Handler) *Route

OPTIONS is a shorthand for RouterGroup.Route with OPTIONS as route method.

func (*RouterGroup) PATCH

func (group *RouterGroup) PATCH(path string, handler Handler) *Route

PATCH is a shorthand for RouterGroup.Route with PATCH as route method.

func (*RouterGroup) POST

func (group *RouterGroup) POST(path string, handler Handler) *Route

POST is a shorthand for RouterGroup.Route with POST as route method.

func (*RouterGroup) PUT

func (group *RouterGroup) PUT(path string, handler Handler) *Route

PUT is a shorthand for RouterGroup.Route with PUT as route method.

func (*RouterGroup) Route

func (group *RouterGroup) Route(method string, path string, handler Handler) *Route

Route registers a single route into the current group.

Note that the final route path will be the concatenation of all parent groups prefixes + the route path. The path follows the standard Go net/http http.ServeMux format ("[HOST]/[PATH]"), meaning that only a top level group route could have HOST as part of the prefix.

Returns the newly created route to allow attaching route-only middlewares.

func (*RouterGroup) SEARCH

func (group *RouterGroup) SEARCH(path string, handler Handler) *Route

SEARCH is a shorthand for RouterGroup.Route with SEARCH as route method.

func (*RouterGroup) Use

func (group *RouterGroup) Use(middlewares ...*Middleware) *RouterGroup

Use registers one or multiple middleware handlers to the current group.

func (*RouterGroup) UseFunc

func (group *RouterGroup) UseFunc(middlewareFuncs ...func(Handler) Handler) *RouterGroup

UseFunc registers one or multiple middleware functions to the current group.

The registered middleware functions are "anonymous" and with default priority, aka. executes in the order they were registered.

If you need to specify a named middleware or middleware with custom exec priority, use RouterGroup.Use method.

Jump to

Keyboard shortcuts

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