router

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2025 License: MIT Imports: 6 Imported by: 1

README

Cosmos: Router

A lightweight HTTP router designed to keep things simple using http.ServeMux. It brings structured routing and elegant middleware chaining to your HTTP apps.


Features

  • Fully generic over http.Handler implementations.
  • Automatic dual route registration (/, /{$}, /path, /path/{$}, etc.).
  • Hierarchical routers with middleware inheritance.
  • Middleware support via simple generic functions.
  • Built-in route existence and handler matching helpers.
  • Friendly to both standard servers and testing workflows.

Example Usage

package main

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

	"github.com/studiolambda/cosmos/router"
)

func logger(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		slog.Info("incoming request request", r.Method, r.URL.Path)
		next.ServeHTTP(w, r)
	})
}

func main() {
	r := router.New[http.Handler]()

	// Use a global middleware
	r.Use(logger)

	// Basic GET route
	r.Get("/hello", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello from cosmos router!")
	}))

	// Grouped routes
	r.Group("/api", func(api *router.Router[http.Handler]) {
		api.Use( /* another middleware to only apply on api */ )

		api.Get("/users", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "List of users")
		}))

		api.With(logger).Post("/users", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintln(w, "Create a user")
		}))
	})

	// Serve on port 8080
	http.ListenAndServe(":8080", r)
}

Install

go get github.com/studiolambda/cosmos/router

Testing Helpers

Use .Record(req) to easily test route output:

req := httptest.NewRequest("GET", "/hello", nil)
res := r.Record(req)
fmt.Println(res.Body.String()) // => "Hello from cosmos router!"

License

MIT © 2025 Èrik C. Forés

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Middleware

type Middleware[H http.Handler] = func(H) H

Middleware is a func type that can be used to apply middleware logic between request and responses.

type Router

type Router[H http.Handler] struct {
	// contains filtered or unexported fields
}

Router is the structure that handles http routing in an application.

This router is completly optional and uses http.ServeMux under the hood to register all the routes.

It also handles some patterns automatically, such as {$}, that is appended on each route automatically, regardless of the pattern.

func New

func New[H http.Handler]() *Router[H]

NewRouter creates a new Router instance and automatically creates all the needed components such as the middleware list or the native http.ServeMux that's used under the hood.

func (*Router[H]) Any

func (router *Router[H]) Any(pattern string, handler H)

Any registers all methods to the given pattern and handler.

func (*Router[H]) Clone

func (router *Router[H]) Clone() *Router[H]

Clone creates a new subrouter and returns it.

func (*Router[H]) Connect

func (router *Router[H]) Connect(pattern string, handler H)

Connect registers a new handler to the router using Router.Method and using the http.MethodConnect as the method parameter.

func (*Router[H]) Delete

func (router *Router[H]) Delete(pattern string, handler H)

Delete registers a new handler to the router using Router.Method and using the http.MethodDelete as the method parameter.

func (*Router[H]) Get

func (router *Router[H]) Get(pattern string, handler H)

Get registers a new handler to the router using Router.Method and using the http.MethodGet as the method parameter.

func (*Router[H]) Group

func (router *Router[H]) Group(pattern string, subrouter func(*Router[H]))

Group uses the given pattern to automatically mount a sub-router that has that pattern as a prefix.

This means that any route registered with the sub-router will also have the given pattern suffixed.

Keep in mind this can be nested as well, meaning that many sub-routers may be grouped, creating complex patterns.

func (*Router[H]) Grouped

func (router *Router[H]) Grouped(subrouter func(*Router[H]))

Grouped clones the router inside a subrouter.

func (*Router[H]) Handler

func (router *Router[H]) Handler(method string, pattern string) (h H, ok bool)

Handler returns the handler that matches the given method and pattern. The second return value determines if the handler was found or not.

For matching against an http.Request use the Router.HandlerMatch method.

func (*Router[H]) HandlerMatch

func (router *Router[H]) HandlerMatch(request *http.Request) (h H, ok bool)

HandlerMatch returns the handler that matches the given http.Request. The second return value determines if the handler was found or not.

For matching against a method and a pattern, use the Router.Handler method.

func (*Router[H]) Has

func (router *Router[H]) Has(method string, pattern string) bool

Has reports whether the given pattern is registered in the router with the given method.

Alternatively, check out the Router.Matches to use an http.Request as the parameter.

func (*Router[H]) Head

func (router *Router[H]) Head(pattern string, handler H)

Head registers a new handler to the router using Router.Method and using the http.MethodHead as the method parameter.

func (*Router[H]) Matches

func (router *Router[H]) Matches(request *http.Request) bool

Matches reports whether the given http.Request match any registered route in the router.

This means that, given the request method and the URL, a handler can be resolved.

func (*Router[H]) Method

func (router *Router[H]) Method(method string, pattern string, handler H)

Method registers a new handler to the router with the given method and pattern. This is usefull if you need to dynamically register a route to the router using a string as the method.

A notable difference is that the patterns's ending slash "/" is not treated as an annonymous catch-all "{...}" and is instead treated as if it finished with "/{$}", making a specific route only.

If the route does not finish in "/", one will be added automatically and then the paragraph above will apply unless the route finishes in a catch-all parameter "...}"

Typically, the method string should be one of the following:

func (*Router[H]) Methods

func (router *Router[H]) Methods(methods []string, pattern string, handler H)

Methods allows binding multiple methods to the pattern and handler.

func (*Router[H]) Options

func (router *Router[H]) Options(pattern string, handler H)

Options registers a new handler to the router using Router.Method and using the http.MethodOptions as the method parameter.

func (*Router[H]) Patch

func (router *Router[H]) Patch(pattern string, handler H)

Patch registers a new handler to the router using Router.Method and using the http.MethodPatch as the method parameter.

func (*Router[H]) Post

func (router *Router[H]) Post(pattern string, handler H)

Post registers a new handler to the router using Router.Method and using the http.MethodPost as the method parameter.

func (*Router[H]) Put

func (router *Router[H]) Put(pattern string, handler H)

Put registers a new handler to the router using Router.Method and using the http.MethodPut as the method parameter.

func (*Router[H]) Record

func (router *Router[H]) Record(r *http.Request) *http.Response

Record returns a http.Response that can be used to inspect what the given http request would have returned as a response.

This method is a shortcut of calling [RecordHandler] with the router as the handler and the given request.

func (*Router[H]) ServeHTTP

func (router *Router[H]) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP is the method that will make the router implement the handler interface, making it possible to be used as a handler in places like http.Server.

func (*Router[H]) Trace

func (router *Router[H]) Trace(pattern string, handler H)

Trace registers a new handler to the router using Router.Method and using the http.MethodTrace as the method parameter.

func (*Router[H]) Use

func (router *Router[H]) Use(middlewares ...Middleware[H])

Use appends to the current router the given middlewares.

Subsequent route registrations will be wrapped with any previous middlewares that the router had defined, plus the new ones that are registered after this call.

In constrats with the Router.With method, this one does modify the current router instead of returning a new sub-router.

func (*Router[H]) With

func (router *Router[H]) With(middlewares ...Middleware[H]) *Router[H]

With does create a new sub-router that automatically applies the given middlewares.

This is very usefull when used to inline some middlewares to specific routes.

In constrast to Router.Use method, it does create a new sub-router instead of modifying the current router.

Jump to

Keyboard shortcuts

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