router

package
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Dec 3, 2025 License: MIT Imports: 9 Imported by: 0

README

Router Package

Overview

The router package produces an http.ServeMux preconfigured with validation, logging, CORS, and timeout middleware. It is designed to sit in front of the auto-generated handlers from oapi-codegen. A functional-options API keeps the defaults lightweight while still allowing any middleware stack to be composed.

Middleware Stack

Requests flow through the following middleware chain in order:

  1. OpenAPI validation: requests are validated against the generated schema using oapi-codegen middleware.
  2. CORS headers: driven by CORSConfig to make cross-origin calls predictable.
  3. Timeout enforcement: ensures slow handlers do not occupy server resources indefinitely.
  4. Structured logging: dumps method, path, headers (with optional redaction), and body size unless the route is listed in QuietdownRoutes.

Configuration

  • Timeout: per-request deadline applied by the HTTP timeout handler.
  • CORS: lists allowed origins, methods, headers, and whether credentials are allowed.
  • QuietdownRoutes: paths that should skip verbose request logging (useful for noisy health checks).
  • HideHeaders: case-insensitive header keys that will be redacted before logging.

Usage Example

swagger, err := openapi3.NewLoader().LoadFromFile("./internal/server/_gen/openapi.json")
if err != nil {
    log.Fatal(err)
}

mux := router.New(
    generatedHandler,
    router.WithSwagger(swagger),
    router.WithLogger(logger),
    router.WithConfig(router.Config{
        Timeout: 5 * time.Second,
        CORS: router.CORSConfig{
            Origins: []string{"https://app.example.com"},
            Methods: []string{"GET", "POST"},
            Headers: []string{"Content-Type", "Authorization"},
            AllowCredentials: true,
        },
        QuietdownRoutes: []string{"/status"},
        HideHeaders:     []string{"Authorization"},
    }),
)

http.ListenAndServe(":8080", mux)

Functional Options

  • router.WithConfig, router.WithConfigMutator: supply or tweak the base Config without losing the defaults.
  • router.WithSwagger, router.WithLogger: make the generated validation and logging middleware aware of the application's observability stack.
  • router.WithMiddlewares, router.WithTrailingMiddlewares: inject custom middleware before or after the default stack.
  • router.WithMiddlewareChain: fully replace the chain with a bespoke one.
  • router.Without* helpers disable any default middleware you don't want.

Pair the router with the api.InfoHandler to expose health and documentation endpoints on the same multiplexer.

Documentation

Overview

Package router wraps http.ServeMux with OpenAPI validation, CORS, timeouts, and logging defaults. ExampleNew_customOptions demonstrates how to combine built-in and custom middlewares.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(apiHandle http.Handler, opts ...Option) *http.ServeMux

New returns a new *http.ServeMux configured with the provided handler and options.

Example (CustomOptions)
package main

import (
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/http/httptest"
	"strings"
	"time"

	"github.com/drblury/apiweaver/router"
)

func main() {
	apiHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "hello")
	})

	mux := router.New(
		apiHandler,
		router.WithLogger(slog.New(slog.NewJSONHandler(io.Discard, nil))),
		router.WithConfig(router.Config{
			Timeout: 2 * time.Second,
			CORS: router.CORSConfig{
				Origins: []string{"https://example.com"},
				Methods: []string{http.MethodGet, http.MethodOptions},
				Headers: []string{"Content-Type"},
			},
			HideHeaders: []string{"Authorization"},
		}),
		router.WithMiddlewares(func(next http.Handler) http.Handler {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				w.Header().Set("X-Stage", "prepend")
				next.ServeHTTP(w, r)
			})
		}),
		router.WithTrailingMiddlewares(func(next http.Handler) http.Handler {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				next.ServeHTTP(w, r)
				w.Header().Set("X-Chain", "completed")
			})
		}),
	)

	req := httptest.NewRequest(http.MethodGet, "/", nil)
	req.Header.Set("Origin", "https://example.com")
	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, req)

	fmt.Println(rec.Header().Get("Access-Control-Allow-Origin"))
	fmt.Println(rec.Header().Get("X-Stage"))
	fmt.Println(rec.Header().Get("X-Chain"))
	fmt.Println(strings.TrimSpace(rec.Body.String()))

}
Output:

https://example.com
prepend
completed
hello

Types

type CORSConfig

type CORSConfig struct {
	AllowCredentials bool
	Headers          []string
	Methods          []string
	Origins          []string
}

CORSConfig configures CORS.

type Config

type Config struct {
	Timeout         time.Duration
	CORS            CORSConfig
	QuietdownRoutes []string
	HideHeaders     []string
}

Config configures the router.

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware wraps an http.Handler to produce a new http.Handler.

type Option

type Option func(*options)

Option configures the router via the functional options pattern.

func WithConfig

func WithConfig(cfg Config) Option

WithConfig replaces the router configuration with the provided value.

func WithConfigMutator

func WithConfigMutator(mutator func(*Config)) Option

WithConfigMutator applies a mutation to the router configuration after defaults are set.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger provides the structured logger to be used by the logging middleware.

func WithMiddlewareChain

func WithMiddlewareChain(middlewares ...Middleware) Option

WithMiddlewareChain fully overrides the middleware chain with the provided sequence.

Example
package main

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

	"github.com/drblury/apiweaver/router"
)

func main() {
	records := make([]string, 0, 4)
	middleware := func(label string) router.Middleware {
		return func(next http.Handler) http.Handler {
			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				records = append(records, label+"-before")
				next.ServeHTTP(w, r)
				records = append(records, label+"-after")
			})
		}
	}

	mux := router.New(
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprint(w, "ok")
		}),
		router.WithMiddlewareChain(
			middleware("first"),
			middleware("second"),
		),
	)

	rec := httptest.NewRecorder()
	mux.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil))

	fmt.Println(rec.Code)
	fmt.Println(records)

}
Output:

200
[first-before second-before second-after first-after]

func WithMiddlewares

func WithMiddlewares(middlewares ...Middleware) Option

WithMiddlewares prepends custom middlewares ahead of the default chain.

func WithSwagger

func WithSwagger(swagger *openapi3.T) Option

WithSwagger wires the OpenAPI document for request validation.

func WithTrailingMiddlewares

func WithTrailingMiddlewares(middlewares ...Middleware) Option

WithTrailingMiddlewares appends middlewares after the default chain.

func WithoutCORSMiddleware

func WithoutCORSMiddleware() Option

WithoutCORSMiddleware disables the CORS middleware regardless of configuration.

func WithoutLoggingMiddleware

func WithoutLoggingMiddleware() Option

WithoutLoggingMiddleware disables the logging middleware.

func WithoutOpenAPIValidation

func WithoutOpenAPIValidation() Option

WithoutOpenAPIValidation disables the OpenAPI validation middleware.

func WithoutTimeoutMiddleware

func WithoutTimeoutMiddleware() Option

WithoutTimeoutMiddleware disables the timeout middleware.

Jump to

Keyboard shortcuts

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