mux

package
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package mux provides helpers for middleware and route handling.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddSpan

func AddSpan(ctx context.Context, spanName string, keyValues ...attribute.KeyValue) (context.Context, trace.Span)

AddSpan adds a span to the tracer, returning it and the context.

func GetTraceID

func GetTraceID(ctx context.Context) string

GetTraceID retrieves the current trace ID from the BaseValue in the given context. We return an empty uuid for testing purposes if not set.

func SetStatusCode

func SetStatusCode(ctx context.Context, statusCode int)

SetStatusCode updates the BaseValue's status code.

Types

type App

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

App is the core web application, managing routing and middleware.

func New

func New(optFns ...Option) *App

New creates an App with the given options. A no-op tracer and the default slog logger are used unless overridden via options.

Example
package main

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

	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	app := mux.New(
		mux.WithLogger(slog.Default()),
	)

	app.Get("/health", func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		fmt.Fprint(w, "ok")
		return nil
	})

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/health", nil)
	app.ServeHTTP(w, r)

	fmt.Println(w.Body.String())
}
Output:
ok

func (*App) Delete

func (a *App) Delete(path string, fn Handler, mw ...Middleware)

Delete registers a handler for DELETE requests at the given path.

func (*App) Get

func (a *App) Get(path string, fn Handler, mw ...Middleware)

Get registers a handler for GET requests at the given path.

Example
package main

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

	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	app := mux.New()
	app.Get("/hello", func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		fmt.Fprint(w, "hello world")
		return nil
	})

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/hello", nil)
	app.ServeHTTP(w, r)

	fmt.Println(w.Code)
	fmt.Println(w.Body.String())
}
Output:
200
hello world

func (*App) Group

func (a *App) Group() *App

Group returns a new App that shares the same underlying ServeMux and tracer but has an independent middleware stack.

Example
package main

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

	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	app := mux.New()

	// Create a group with an independent middleware stack.
	api := app.Group()
	api.Use(func(handler mux.Handler) mux.Handler {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			w.Header().Set("X-API", "true")
			return handler(ctx, w, r)
		}
	})

	api.Get("/api/data", func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		fmt.Fprint(w, "data")
		return nil
	})

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/api/data", nil)
	app.ServeHTTP(w, r)

	fmt.Println(w.Header().Get("X-API"))
	fmt.Println(w.Body.String())
}
Output:
true
data

func (*App) Handle

func (a *App) Handle(method, group, path string, handler Handler, mw ...Middleware)

func (*App) HandleNoMiddleware

func (a *App) HandleNoMiddleware(method, group, path string, handler Handler)

HandleNoMiddleware registers a handler without wrapping it in the route-level or group-level middleware stack.

func (*App) HandleRaw

func (a *App) HandleRaw(method, group, path string, handler http.Handler, mw ...Middleware)

func (*App) Mount

func (a *App) Mount(subRoute string) *App

Mount returns a new App scoped to the given sub-route prefix. All routes registered on the returned App are prefixed with subRoute.

Example
package main

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

	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	app := mux.New()

	v1 := app.Mount("/v1")
	v1.Get("/users", func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		fmt.Fprint(w, "v1 users")
		return nil
	})

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/v1/users", nil)
	app.ServeHTTP(w, r)

	fmt.Println(w.Body.String())
}
Output:
v1 users

func (*App) Patch

func (a *App) Patch(path string, fn Handler, mw ...Middleware)

Patch registers a handler for PATCH requests at the given path.

func (*App) Post

func (a *App) Post(path string, fn Handler, mw ...Middleware)

Post registers a handler for POST requests at the given path.

Example
package main

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

	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	app := mux.New()
	app.Post("/items", func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		w.WriteHeader(http.StatusCreated)
		fmt.Fprint(w, "created")
		return nil
	})

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodPost, "/items", nil)
	app.ServeHTTP(w, r)

	fmt.Println(w.Code)
	fmt.Println(w.Body.String())
}
Output:
201
created

func (*App) Put

func (a *App) Put(path string, fn Handler, mw ...Middleware)

Put registers a handler for PUT requests at the given path.

func (*App) ServeHTTP

func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler, wrapping global middleware before serving the request.

func (*App) Use

func (a *App) Use(mw ...Middleware)

Use appends the given middleware to the underlying mw stack.

Example
package main

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

	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	app := mux.New()

	app.Use(func(handler mux.Handler) mux.Handler {
		return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
			w.Header().Set("X-Custom", "active")
			return handler(ctx, w, r)
		}
	})

	app.Get("/ping", func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		fmt.Fprint(w, "pong")
		return nil
	})

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/ping", nil)
	app.ServeHTTP(w, r)

	fmt.Println(w.Header().Get("X-Custom"))
	fmt.Println(w.Body.String())
}
Output:
active
pong

type BaseValues

type BaseValues struct {
	TraceID    string
	Now        time.Time
	Tracer     trace.Tracer
	StatusCode int
}

BaseValues represents values that are shared across all requests for logging.

func GetValues

func GetValues(ctx context.Context) *BaseValues

GetValues retrieves the BaseValues from the given context.

type Handler

type Handler func(ctx context.Context, w http.ResponseWriter, r *http.Request) error

Handler is a http.Handler that returns an error.

type Middleware

type Middleware func(handler Handler) Handler

Middleware defines a signature to chain Handler together.

type Option

type Option func(*options)

func WithLogger

func WithLogger(log *slog.Logger) Option

WithLogger sets the logger used by the App for internal errors.

func WithMiddleware

func WithMiddleware(mw ...Middleware) Option

WithMiddleware auto-categorizes the given middleware by function name, assigns priorities, and splits them into global vs route-level stacks. Known global middleware (CORS, CSRF) runs on every request via ServeHTTP. Known route middleware (Logger, Errors, Panics) and any custom middleware run per-route in priority order.

Example
package main

import (
	"fmt"
	"log/slog"

	"github.com/adamwoolhether/httper/web/middleware"
	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	// WithMiddleware auto-categorizes by function name:
	// CORS → global, Logger/Errors → route-level, Panics → outermost route-level.
	app := mux.New(
		mux.WithMiddleware(
			middleware.CORS([]string{"*"}),
			middleware.Logger(slog.Default()),
			middleware.Errors(slog.Default()),
			middleware.Panics(),
		),
	)

	_ = app
	fmt.Println("middleware auto-categorized")
}
Output:
middleware auto-categorized

func WithStaticFS

func WithStaticFS(fsys fs.FS, pathPrefix string) Option

WithStaticFS serves static files from fsys under the given URL path prefix. The prefix is stripped before looking up files in fsys.

Example
package main

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

	"github.com/adamwoolhether/httper/web/mux"
)

func main() {
	static := fstest.MapFS{
		"hello.txt": &fstest.MapFile{Data: []byte("static content")},
	}

	app := mux.New(
		mux.WithStaticFS(static, "/static/"),
	)

	w := httptest.NewRecorder()
	r := httptest.NewRequest(http.MethodGet, "/static/hello.txt", nil)
	app.ServeHTTP(w, r)

	fmt.Println(w.Code)
	fmt.Println(w.Body.String())
}
Output:
200
static content

func WithTracer

func WithTracer(tracer trace.Tracer) Option

WithTracer injects the given tracer into the App.

Jump to

Keyboard shortcuts

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