rtr

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 19, 2025 License: AGPL-3.0 Imports: 9 Imported by: 0

README

HTTP Router Package

A flexible and feature-rich HTTP router implementation for Go applications that supports route grouping, middleware chains, and nested routing structures.

Tests Status Go Report Card PkgGoDev License

Features

  • Route Management: Define and manage HTTP routes with support for all standard HTTP methods using exact path matching
  • Route Groups: Group related routes with shared prefixes and middleware
  • Middleware Support:
    • Pre-route (before) middleware
    • Post-route (after) middleware
    • Support at router, group, and individual route levels
    • Built-in panic recovery middleware
  • Nested Groups: Create hierarchical route structures with nested groups
  • Flexible API: Chainable methods for intuitive route and group configuration
  • Standard Interface: Implements http.Handler interface for seamless integration

Middleware

Built-in Middleware
Recovery Middleware

The router includes a built-in recovery middleware that catches panics in your handlers and returns a 500 Internal Server Error response instead of crashing the server. This middleware is added by default when you create a new router with NewRouter().

// This is automatically added when you create a new router
router := router.NewRouter()

// But you can also add it manually if needed
router.AddBeforeMiddlewares([]router.Middleware{router.RecoveryMiddleware})
Custom Middleware

You can create your own middleware by implementing the Middleware type:

func myMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Do something before the handler runs
        log.Println("Before handler")
        
        // Call the next handler
        next.ServeHTTP(w, r)
        
        // Do something after the handler runs
        log.Println("After handler")
    })
}

// Add it to your router
router.AddBeforeMiddlewares([]router.Middleware{myMiddleware})

Core Components

Router

The main router component that handles HTTP requests and manages routes and groups.

router := router.NewRouter()
Routes

Individual route definitions that specify HTTP method, path, and handler.

// Using shortcut methods
route := router.Get("/users", handleUsers)      // Exact match: /users
route := router.Post("/users", createUser)     // Exact match: /users
route := router.Put("/users/123", updateUser)  // Exact match required: /users/123
route := router.Delete("/users/123", deleteUser) // Exact match required: /users/123

// Using method chaining
route := router.NewRoute()
    .SetMethod("GET")
    .SetPath("/users")
    .SetHandler(handleUsers)
Groups

Route groups that share common prefixes and middleware.

group := router.NewGroup()
    .SetPrefix("/api")
    .AddRoute(route)

Usage Examples

Basic Router Setup
r := router.NewRouter()

// Add routes using shortcut methods
r.AddRoute(router.Get("/hello", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
}))

// Add routes using method chaining
r.AddRoute(router.NewRoute()
    .SetMethod("GET")
    .SetPath("/users")
    .SetHandler(handleUsers))
Using Route Groups
// Create an API group
apiGroup := router.NewGroup().SetPrefix("/api")

// Add routes to the group
apiGroup.AddRoute(router.NewRoute()
    .SetMethod("GET")
    .SetPath("/users")
    .SetHandler(handleUsers))

// Add the group to the router
r.AddGroup(apiGroup)
Adding Middleware
// Router-level middleware
r.AddBeforeMiddlewares([]router.Middleware{
    loggingMiddleware,
    authenticationMiddleware,
})

// Group-level middleware
apiGroup.AddBeforeMiddlewares([]router.Middleware{
    apiKeyMiddleware,
})

// Route-level middleware
route.AddBeforeMiddlewares([]router.Middleware{
    specificRouteMiddleware,
})

Path Parameters

The router supports flexible path parameter extraction with the following features:

Basic Parameters

Extract values from URL paths using :param syntax:

// Define a route with parameters
r.AddRoute(rtr.NewRoute().
    SetMethod("GET").
    SetPath("/users/:id").
    SetHandler(func(w http.ResponseWriter, r *http.Request) {
        // Get a required parameter
        id := rtr.MustGetParam(r, "id")
        
        // Or safely get an optional parameter
        if name, exists := rtr.GetParam(r, "name"); exists {
            // Parameter exists
        }
    }))
Optional Parameters

Mark parameters as optional with ?:

// Both /articles/tech and /articles/tech/123 will match
r.AddRoute(rtr.NewRoute().
    SetMethod("GET").
    SetPath("/articles/:category/:id?").
    SetHandler(handleArticle))
Wildcard/Catch-all Routes

Use * to match all remaining path segments:

// Matches /static/js/main.js, /static/css/style.css, etc.
r.AddRoute(rtr.NewRoute().
    SetMethod("GET").
    SetPath("/static/*filepath").
    SetHandler(serveStaticFile))
Getting All Parameters

Retrieve all path parameters as a map:

params := rtr.GetParams(r)
// params is a map[string]string of all path parameters

Path Matching Rules

The router uses the following matching rules:

  • Paths are matched exactly as defined, with parameter placeholders
  • Required parameters must be present in the request path
  • Optional parameters can be omitted
  • Parameter names must be unique within a route
  • The wildcard parameter must be the last segment in the path

Domain-based Routing

The router supports domain-based routing, allowing you to define routes that only match specific domain names or patterns.

Creating a Domain
// Create a domain with exact match
domain := router.NewDomain("example.com")

// Create a domain with wildcard subdomain matching
wildcardDomain := router.NewDomain("*.example.com")

// Create a domain that matches multiple patterns
multiDomain := router.NewDomain("example.com", "api.example.com", "*.example.org")
Adding Routes to a Domain
// Create a new domain
domain := router.NewDomain("api.example.com")

// Add routes directly to the domain
domain.AddRoute(router.Get("/users", handleUsers))

// Add multiple routes at once
domain.AddRoutes([]router.RouteInterface{
    router.Get("/users", handleUsers),
    router.Post("/users", createUser),
})
Adding Groups to a Domain
// Create a domain
domain := router.NewDomain("api.example.com")

// Create an API group
apiGroup := router.NewGroup().SetPrefix("/v1")

// Add routes to the group
apiGroup.AddRoute(router.Get("/products", handleProducts))

// Add the group to the domain
domain.AddGroup(apiGroup)

// Add the domain to the router
router.AddDomain(domain)
Domain Matching

Domains are matched against the Host header of incoming requests. The matching supports:

Basic Domain Matching
  • Exact matches (example.com)
  • Wildcard subdomains (*.example.com)
  • Multiple patterns per domain
Port Matching
  • No port in pattern: Matches any port on that host

    domain := router.NewDomain("example.com")  // Matches example.com, example.com:8080, example.com:3000, etc.
    
  • Exact port: Requires exact port match

    domain := router.NewDomain("example.com:8080")  // Only matches example.com:8080
    
  • Wildcard port: Matches any port on that host

    domain := router.NewDomain("example.com:*")  // Matches example.com with any port
    
  • IPv4 and IPv6 support:

    // IPv4 with port
    ipv4Domain := router.NewDomain("127.0.0.1:8080")  // Matches 127.0.0.1:8080
    
    // IPv6 with port (note the square brackets)
    ipv6Domain := router.NewDomain("[::1]:8080")  // Matches [::1]:8080
    
Examples
// Match any port on example.com
anyPort := router.NewDomain("example.com")

// Match only port 8080
exactPort := router.NewDomain("example.com:8080")

// Match any subdomain on any port
wildcardSubdomain := router.NewDomain("*.example.com:*")

// Match localhost on any port
localhost := router.NewDomain("localhost:*")

// Match IPv6 localhost on port 3000
ipv6Localhost := router.NewDomain("[::1]:3000")
Middleware on Domains

Middleware can be added at the domain level to apply to all routes within that domain:

domain := router.NewDomain("admin.example.com")

// Add middleware that will run before all routes in this domain
domain.AddBeforeMiddlewares([]router.Middleware{
    adminAuthMiddleware,
    loggingMiddleware,
})

// Add middleware that will run after all routes in this domain
domain.AddAfterMiddlewares([]router.Middleware{
    responseTimeMiddleware,
})

Interfaces

RouterInterface

The main router interface that provides methods for managing routes and groups:

  • GetPrefix() / SetPrefix(): Manage router prefix
  • AddGroup() / AddGroups(): Add route groups
  • AddRoute() / AddRoutes(): Add individual routes
  • AddBeforeMiddlewares() / AddAfterMiddlewares(): Add middleware chains
  • ServeHTTP(): Handle HTTP requests
GroupInterface

Interface for managing route groups:

  • GetPrefix() / SetPrefix(): Manage group prefix
  • AddRoute() / AddRoutes(): Add routes to the group
  • AddGroup() / AddGroups(): Add nested groups
  • AddBeforeMiddlewares() / AddAfterMiddlewares(): Add group-level middleware
RouteInterface

Interface for configuring individual routes:

  • GetMethod() / SetMethod(): HTTP method configuration
  • GetPath() / SetPath(): URL path configuration
  • GetHandler() / SetHandler(): Route handler configuration
  • GetName() / SetName(): Route naming
  • AddBeforeMiddlewares() / AddAfterMiddlewares(): Route-specific middleware
Shortcut Methods

The package provides shortcut methods for common HTTP methods:

  • Get(path string, handler Handler) RouteInterface - Creates a GET route
  • Post(path string, handler Handler) RouteInterface - Creates a POST route
  • Put(path string, handler Handler) RouteInterface - Creates a PUT route
  • Delete(path string, handler Handler) RouteInterface - Creates a DELETE route

These methods automatically set the HTTP method, path, and handler, making route creation more concise.

Testing

The package includes comprehensive test coverage:

  • router_test.go: Core router functionality tests
  • router_integration_test.go: Integration tests
  • route_test.go: Route-specific tests
  • group_test.go: Group functionality tests
  • examples/basic/: Complete example with tests

Run tests using:

# From the root directory
go test .

# Or to run all tests including examples
go test ./...

Documentation

Index

Constants

View Source
const (
	// ParamsKey is the key used to store path parameters in the request context
	ParamsKey contextKey = "params"
)

Context key for storing path parameters in the request context

Variables

This section is empty.

Functions

func GetParam added in v0.1.3

func GetParam(r *http.Request, name string) (string, bool)

GetParam retrieves a path parameter from the request context by name. Returns the parameter value and true if found, or an empty string and false otherwise.

func GetParams added in v0.1.3

func GetParams(r *http.Request) map[string]string

GetParams returns all path parameters as a map. Returns an empty map if no parameters exist.

func MustGetParam added in v0.1.3

func MustGetParam(r *http.Request, name string) string

MustGetParam retrieves a path parameter from the request context by name. Panics if the parameter is not found. Use only when you're certain the parameter exists.

func RecoveryMiddleware

func RecoveryMiddleware(next http.Handler) http.Handler

RecoveryMiddleware creates a new middleware that recovers from panics. It logs the panic details and returns a 500 Internal Server Error response. This should typically be added as one of the first middlewares in the chain.

Types

type DomainInterface

type DomainInterface interface {
	// GetPatterns returns the domain patterns that this domain matches against
	GetPatterns() []string

	// SetPatterns sets the domain patterns for this domain and returns the domain for method chaining
	SetPatterns(patterns ...string) DomainInterface

	// AddRoute adds a route to this domain and returns the domain for method chaining
	AddRoute(route RouteInterface) DomainInterface

	// AddRoutes adds multiple routes to this domain and returns the domain for method chaining
	AddRoutes(routes []RouteInterface) DomainInterface

	// GetRoutes returns all routes that belong to this domain
	GetRoutes() []RouteInterface

	// AddGroup adds a group to this domain and returns the domain for method chaining
	AddGroup(group GroupInterface) DomainInterface

	// AddGroups adds multiple groups to this domain and returns the domain for method chaining
	AddGroups(groups []GroupInterface) DomainInterface

	// GetGroups returns all groups that belong to this domain
	GetGroups() []GroupInterface

	// AddBeforeMiddlewares adds middleware functions to be executed before any route handler in this domain
	// Returns the domain for method chaining
	AddBeforeMiddlewares(middleware []Middleware) DomainInterface

	// GetBeforeMiddlewares returns all middleware functions that will be executed before any route handler in this domain
	GetBeforeMiddlewares() []Middleware

	// AddAfterMiddlewares adds middleware functions to be executed after any route handler in this domain
	// Returns the domain for method chaining
	AddAfterMiddlewares(middleware []Middleware) DomainInterface

	// GetAfterMiddlewares returns all middleware functions that will be executed after any route handler in this domain
	GetAfterMiddlewares() []Middleware

	// Match checks if the given host matches any of this domain's patterns
	Match(host string) bool
}

DomainInterface defines the interface for a domain that can have routes and groups. A domain represents a collection of routes and groups that are only accessible when the request's Host header matches the domain's patterns.

func NewDomain

func NewDomain(patterns ...string) DomainInterface

NewDomain creates a new domain with the given patterns

type GroupInterface

type GroupInterface interface {
	// GetPrefix returns the URL path prefix associated with this group.
	GetPrefix() string
	// SetPrefix sets the URL path prefix for this group and returns the group for method chaining.
	SetPrefix(prefix string) GroupInterface

	// AddRoute adds a single route to this group and returns the group for method chaining.
	AddRoute(route RouteInterface) GroupInterface
	// AddRoutes adds multiple routes to this group and returns the group for method chaining.
	AddRoutes(routes []RouteInterface) GroupInterface
	// GetRoutes returns all routes that belong to this group.
	GetRoutes() []RouteInterface

	// AddGroup adds a single nested group to this group and returns the group for method chaining.
	AddGroup(group GroupInterface) GroupInterface
	// AddGroups adds multiple nested groups to this group and returns the group for method chaining.
	AddGroups(groups []GroupInterface) GroupInterface
	// GetGroups returns all nested groups that belong to this group.
	GetGroups() []GroupInterface

	// AddBeforeMiddlewares adds middleware functions to be executed before any route handler in this group.
	// Returns the group for method chaining.
	AddBeforeMiddlewares(middleware []Middleware) GroupInterface
	// GetBeforeMiddlewares returns all middleware functions that will be executed before any route handler in this group.
	GetBeforeMiddlewares() []Middleware

	// AddAfterMiddlewares adds middleware functions to be executed after any route handler in this group.
	// Returns the group for method chaining.
	AddAfterMiddlewares(middleware []Middleware) GroupInterface
	// GetAfterMiddlewares returns all middleware functions that will be executed after any route handler in this group.
	GetAfterMiddlewares() []Middleware
}

GroupInterface defines the interface for a group of routes. A group represents a collection of routes that share common properties such as a URL prefix and middleware. Groups can also be nested to create hierarchical route structures.

func NewGroup

func NewGroup() GroupInterface

NewGroup creates and returns a new GroupInterface implementation. This is used to create a new route group that can be added to a router.

type Handler

type Handler func(http.ResponseWriter, *http.Request)

Handler defines the function signature for HTTP request handlers.

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware represents a middleware function. It is a function type that takes an http.Handler and returns an http.Handler. Middleware functions can be used to process requests before or after they reach the main handler.

func DefaultMiddlewares

func DefaultMiddlewares() []Middleware

DefaultMiddlewares returns a slice of default middlewares that should be used with the router. Currently, it only includes the RecoveryMiddleware.

type MiddlewareInfo added in v0.2.0

type MiddlewareInfo struct {
	Name string
	Func Middleware
}

MiddlewareInfo represents middleware information for display purposes

type RouteInterface

type RouteInterface interface {
	// GetMethod returns the HTTP method associated with this route.
	GetMethod() string
	// SetMethod sets the HTTP method for this route and returns the route for method chaining.
	SetMethod(method string) RouteInterface

	// GetPath returns the URL path pattern associated with this route.
	GetPath() string
	// SetPath sets the URL path pattern for this route and returns the route for method chaining.
	SetPath(path string) RouteInterface

	// GetHandler returns the handler function associated with this route.
	GetHandler() Handler
	// SetHandler sets the handler function for this route and returns the route for method chaining.
	SetHandler(handler Handler) RouteInterface

	// GetName returns the name identifier associated with this route.
	GetName() string
	// SetName sets the name identifier for this route and returns the route for method chaining.
	SetName(name string) RouteInterface

	// AddBeforeMiddlewares adds middleware functions to be executed before the route handler.
	// Returns the route for method chaining.
	AddBeforeMiddlewares(middleware []Middleware) RouteInterface
	// GetBeforeMiddlewares returns all middleware functions that will be executed before the route handler.
	GetBeforeMiddlewares() []Middleware

	// AddAfterMiddlewares adds middleware functions to be executed after the route handler.
	// Returns the route for method chaining.
	AddAfterMiddlewares(middleware []Middleware) RouteInterface
	// GetAfterMiddlewares returns all middleware functions that will be executed after the route handler.
	GetAfterMiddlewares() []Middleware
}

RouteInterface defines the interface for a single route definition. A route represents a mapping between an HTTP method, a URL path pattern, and a handler function. Routes can also have associated middleware that will be executed before or after the handler.

func Delete

func Delete(path string, handler Handler) RouteInterface

Delete creates a new DELETE route with the given path and handler It is a shortcut method that combines setting the method to DELETE, path, and handler.

func Get

func Get(path string, handler Handler) RouteInterface

Get creates a new GET route with the given path and handler It is a shortcut method that combines setting the method to GET, path, and handler.

func NewRoute

func NewRoute() RouteInterface

This is used to create a new route that can be added to a router or group.

func Post

func Post(path string, handler Handler) RouteInterface

Post creates a new POST route with the given path and handler It is a shortcut method that combines setting the method to POST, path, and handler.

func Put

func Put(path string, handler Handler) RouteInterface

Put creates a new PUT route with the given path and handler It is a shortcut method that combines setting the method to PUT, path, and handler.

type RouterInterface

type RouterInterface interface {
	// GetPrefix returns the URL path prefix associated with this router.
	GetPrefix() string
	// SetPrefix sets the URL path prefix for this router and returns the router for method chaining.
	// The prefix will be prepended to all routes in this router.
	SetPrefix(prefix string) RouterInterface

	// AddGroup adds a single group to this router and returns the router for method chaining.
	// The group's prefix will be combined with the router's prefix for all routes in the group.
	AddGroup(group GroupInterface) RouterInterface
	// AddGroups adds multiple groups to this router and returns the router for method chaining.
	// Each group's prefix will be combined with the router's prefix for all routes in the group.
	AddGroups(groups []GroupInterface) RouterInterface
	// GetGroups returns all groups that belong to this router.
	// Returns a slice of GroupInterface implementations.
	GetGroups() []GroupInterface

	// AddRoute adds a single route to this router and returns the router for method chaining.
	// The route's path will be prefixed with the router's prefix.
	AddRoute(route RouteInterface) RouterInterface
	// AddRoutes adds multiple routes to this router and returns the router for method chaining.
	// Each route's path will be prefixed with the router's prefix.
	AddRoutes(routes []RouteInterface) RouterInterface
	// GetRoutes returns all routes that belong to this router.
	// Returns a slice of RouteInterface implementations.
	GetRoutes() []RouteInterface

	// AddBeforeMiddlewares adds middleware functions to be executed before any route handler.
	// The middleware functions will be executed in the order they are added.
	// Returns the router for method chaining.
	AddBeforeMiddlewares(middleware []Middleware) RouterInterface
	// GetBeforeMiddlewares returns all middleware functions that will be executed before any route handler.
	// Returns a slice of Middleware functions.
	GetBeforeMiddlewares() []Middleware

	// AddAfterMiddlewares adds middleware functions to be executed after any route handler.
	// The middleware functions will be executed in reverse order of how they were added.
	// Returns the router for method chaining.
	AddAfterMiddlewares(middleware []Middleware) RouterInterface
	// GetAfterMiddlewares returns all middleware functions that will be executed after any route handler.
	// Returns a slice of Middleware functions.
	GetAfterMiddlewares() []Middleware

	// AddDomain adds a domain to this router and returns the router for method chaining
	AddDomain(domain DomainInterface) RouterInterface

	// AddDomains adds multiple domains to this router and returns the router for method chaining
	AddDomains(domains []DomainInterface) RouterInterface

	// GetDomains returns all domains that belong to this router
	GetDomains() []DomainInterface

	// List displays the router's configuration in formatted tables for debugging and documentation
	// Shows global middleware, domains, direct routes, and route groups
	List()

	// ServeHTTP implements the http.Handler interface.
	// It matches the incoming request to the appropriate route and executes the handler.
	ServeHTTP(w http.ResponseWriter, r *http.Request)
}

RouterInterface defines the interface for a router that can handle HTTP requests. A router is responsible for matching incoming HTTP requests to the appropriate route handler and executing any associated middleware.

func NewRouter

func NewRouter() RouterInterface

NewRouter creates and returns a new RouterInterface implementation. This is the main entry point for creating a new router. By default, it includes recovery middleware to handle panics.

Directories

Path Synopsis
examples
basic command
domain command
path-parameters command

Jump to

Keyboard shortcuts

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