middlewares

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: May 4, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CORS

func CORS(cfg CORSConfig) func(http.Handler) http.Handler

CORS returns a fully configured CORS middleware using rs/cors under the hood.

r.Use(mw.CORS(mw.CORSConfig{
    AllowedOrigins:   []string{"https://app.example.com"},
    AllowCredentials: true,
}))

func CachePrivate

func CachePrivate(maxAge time.Duration) func(http.Handler) http.Handler

CachePrivate sets Cache-Control to private with the given max-age. Use on user-specific responses that the browser may cache but proxies must not.

r.With(mw.CachePrivate(1 * time.Minute)).Get("/dashboard/stats", handler)

func CachePublic

func CachePublic(maxAge time.Duration) func(http.Handler) http.Handler

CachePublic sets Cache-Control to public with the given max-age. Use on responses that are safe to cache by anyone (CDN, browser, proxy).

r.With(mw.CachePublic(5 * time.Minute)).Get("/assets/config", handler)

func Handler

func Handler() http.Handler

Handler returns a standard Prometheus /metrics HTTP handler.

func Logs

func Logs(cfg Config) func(http.Handler) http.Handler

Logs returns structured request logging middleware. It reads the request ID from the header set by the RequestID middleware (or any upstream proxy).

func MaxBodySize

func MaxBodySize(limit int64) func(http.Handler) http.Handler

MaxBodySize limits the request body to limit bytes. Requests with bodies exceeding the limit will fail when the handler reads past it. Only applied when the request body is non-nil.

func Metrics

func Metrics(c *Collectors, cfg MetricsConfig) func(http.Handler) http.Handler

Metrics returns an HTTP middleware that records request count and duration.

func NoCache

func NoCache() func(http.Handler) http.Handler

NoCache sets headers that instruct clients and proxies never to cache the response. Use on endpoints that always return fresh data (auth, user-specific, etc).

r.With(mw.NoCache()).Get("/me", handler)

func QueryParams

func QueryParams[T any](r *http.Request) T

QueryParams extracts the parsed query params from the request context.

func RateLimit

func RateLimit(cfg RateLimitConfig) func(http.Handler) http.Handler

RateLimit returns a token-bucket rate limiting middleware. Each unique key (by default, the client IP) gets its own limiter. Requests that exceed the limit receive 429 Too Many Requests.

// 100 req/s per IP, burst of 20
r.Use(mw.RateLimit(mw.RateLimitConfig{RPS: 100, Burst: 20}))

// per API key
r.Use(mw.RateLimit(mw.RateLimitConfig{
    RPS:   50,
    Burst: 10,
    KeyExtractor: func(r *http.Request) string {
        return r.Header.Get("X-API-Key")
    },
}))

func RealIP

func RealIP() func(http.Handler) http.Handler

RealIP rewrites r.RemoteAddr with the real client IP, in order:

  1. CF-Connecting-IP — set by Cloudflare, cannot be spoofed by clients
  2. X-Forwarded-For — leftmost (client) IP, used for direct/non-Cloudflare traffic
  3. RemoteAddr — unchanged fallback for direct connections with no proxy headers

Place this as early as possible in the middleware chain so that downstream middleware (e.g. RateLimit) see the correct IP.

r.Use(mw.RealIP())

func Recover

func Recover(logger *zap.Logger) func(http.Handler) http.Handler

Recover catches panics, logs them via zap with the full stack trace, and responds with 500 Internal Server Error.

r.Use(mw.Recover(logger))

func RequestID

func RequestID(cfg ...RequestIDConfig) func(http.Handler) http.Handler

RequestID adds a request ID to the context and response headers. If the incoming request already carries one in the configured header it is reused; otherwise a new UUID v7 is generated (falling back to v4 on error).

func RequestIDFromCtx

func RequestIDFromCtx(ctx context.Context) string

RequestIDFromCtx retrieves the request ID stored by the RequestID middleware.

func SecurityHeaders

func SecurityHeaders() func(http.Handler) http.Handler

SecurityHeaders sets a baseline of security-related response headers suitable for APIs. For HTML-serving applications consider also adding Content-Security-Policy and Permissions-Policy.

r.Use(mw.SecurityHeaders())

func Timeout

func Timeout(d time.Duration) func(http.Handler) http.Handler

Timeout sets a deadline on the request context. Handlers must respect ctx.Done() to honour the timeout — this is especially important for SSE and WebSocket handlers which own their own read/write loops. Pass 0 to use the default of 30s.

r.Use(mw.Timeout(10 * time.Second))
r.Use(mw.Timeout(0)) // 30s default

func WithParams

func WithParams[T any](strict ...bool) func(http.Handler) http.Handler

WithParams parses, converts, and validates query params into T using fun_query tags. Pass strict=true to reject any query param not declared in T.

Tags:

fun_query:"key"               string param, optional
fun_query:"key,required"      param must be present and non-empty
fun_query:"key,default=val"   fallback value if absent or empty

Supported field types: string, int, int64, bool, uuid.UUID

Usage:

type ListParams struct {
    Status string    `fun_query:"status,default=active"`
    Page   int       `fun_query:"page,default=1"`
    ID     uuid.UUID `fun_query:"id,required"`
}

r.With(middlewares.WithParams[ListParams]()).Get("/items", h.List)
r.With(middlewares.WithParams[ListParams](true)).Get("/items", h.List) // strict

// in handler:
params := middlewares.Params[ListParams](r)

Types

type APIKeyHook

type APIKeyHook func(ctx context.Context, rawKey string) (context.Context, error)

APIKeyHook is called with the raw key extracted from X-API-Key. Lookup, hashing, and ctx enrichment are fully the caller's responsibility. Returning a non-nil error rejects the request with 401.

type CORSConfig

type CORSConfig struct {
	// AllowedOrigins is the list of origins allowed to make cross-origin requests.
	// Use []string{"*"} to allow all. Required.
	AllowedOrigins []string

	// AllowedMethods defaults to GET, POST, PUT, PATCH, DELETE, OPTIONS if empty.
	AllowedMethods []string

	// AllowedHeaders defaults to Content-Type, Authorization, X-Request-ID, X-API-Key if empty.
	AllowedHeaders []string

	// ExposedHeaders are headers the browser is allowed to read from the response.
	ExposedHeaders []string

	// AllowCredentials sets Access-Control-Allow-Credentials.
	// Note: cannot be used with AllowedOrigins: ["*"].
	AllowCredentials bool

	// MaxAge sets how long the preflight result can be cached.
	// Defaults to 10 minutes if zero.
	MaxAge time.Duration
}

CORSConfig exposes the options you actually need to touch. Everything else is defaulted to safe, permissive-for-APIs values.

type Collectors

type Collectors struct {
	RequestsTotal   *prometheus.CounterVec
	RequestDuration *prometheus.HistogramVec
}

Collectors holds the Prometheus metrics registered by this package. Use NewCollectors to create and register them, then pass to Middleware.

func NewCollectors

func NewCollectors(reg prometheus.Registerer) (*Collectors, error)

NewCollectors creates and registers Prometheus metrics with the given registerer. Pass prometheus.DefaultRegisterer if you don't have a custom one.

type Config

type Config struct {
	// Logger is the zap logger to write to. Required.
	Logger *zap.Logger

	// SkipPrefixes lists URL path prefixes that will not be logged.
	// e.g. []string{"/admin/asynq", "/healthz"}
	SkipPrefixes []string

	// RequestIDHeader is the header name to read the request ID from.
	// Defaults to "X-Request-ID".
	RequestIDHeader string
}

Config controls logger behavior.

type JWTHook

type JWTHook[C jwt.Claims] func(ctx context.Context, claims C) (context.Context, error)

JWTHook is called after the package successfully verifies and parses a JWT. It may enrich ctx (e.g. inject a subject) and must return the (possibly new) ctx. Returning a non-nil error rejects the request with 401.

type KeyExtractorFunc

type KeyExtractorFunc func(r *http.Request) string

KeyExtractorFunc extracts a rate limit key from the request. The returned string is used to bucket requests — typically an IP or API key.

type KeyFunc

type KeyFunc[C jwt.Claims] func(ctx context.Context, tokenStr string) (C, error)

KeyFunc returns the key used to verify a token, given its parsed (but unverified) form while also taking a context. Mirrors jwt.Keyfunc so callers can reuse existing implementations.

type MetricsConfig

type MetricsConfig struct {
	// SkipPrefixes lists URL path prefixes that will not be instrumented.
	// e.g. []string{"/metrics", "/swagger", "/healthz"}
	SkipPrefixes []string
}

MetricsConfig controls which paths are skipped by the middleware.

type Middleware

type Middleware[C jwt.Claims] struct {
	KeyFunc KeyFunc[C]
	// contains filtered or unexported fields
}

Middleware holds all auth configuration. Build once with New, use everywhere.

func New

func New[C jwt.Claims](keyFunc KeyFunc[C], jwtHook JWTHook[C], apiKeyHook APIKeyHook) *Middleware[C]

New creates an auth middleware.

  • keyFunc: provides the verification key (e.g. []byte HMAC secret, *rsa.PublicKey)
  • jwtHook: called after successful JWT verification to enrich the ctx
  • apiKeyHook: called with the raw API key — lookup and ctx enrichment are yours

TracerName optionally overrides the OTel tracer name (defaults to "trimid/auth").

authMW := auth.New[*MyClaims](keyFunc, jwtHook, apiKeyHook)
r.With(authMW.JWT()).Get("/me", meHandler)
r.With(authMW.APIKey()).Post("/ingest", ingestHandler)
r.With(authMW.AnyAuth()).Post("/events", eventHandler)

func (*Middleware[C]) APIKey

func (m *Middleware[C]) APIKey() func(http.Handler) http.Handler

APIKey returns a middleware that:

  1. Extracts the raw key from X-API-Key.
  2. Calls the configured APIKeyHook — lookup, hashing, and ctx enrichment are yours.

Any failure at any step responds 401 and stops the chain.

func (*Middleware[C]) AnyAuth

func (m *Middleware[C]) AnyAuth() func(http.Handler) http.Handler

AnyAuth tries API key first (if X-API-Key is present), then Bearer JWT. At least one must succeed; if neither header is present the request is rejected.

func (*Middleware[C]) JWT

func (m *Middleware[C]) JWT() func(http.Handler) http.Handler

JWT returns a middleware that:

  1. Extracts the Bearer token from Authorization.
  2. Verifies and parses it into C using the configured KeyFunc.
  3. Calls the configured JWTHook with the parsed claims.

Any failure at any step responds 401 and stops the chain.

type RateLimitConfig

type RateLimitConfig struct {
	// RPS is the number of requests per second allowed per key.
	RPS rate.Limit

	// Burst is the maximum burst size above RPS.
	Burst int

	// KeyExtractor determines how to bucket requests.
	// Defaults to RemoteAddr (IP-based) if nil.
	KeyExtractor KeyExtractorFunc
}

RateLimitConfig configures the rate limiter.

type RequestIDConfig

type RequestIDConfig struct {
	// Header is the header to read/write the request ID.
	// Defaults to "X-Request-ID".
	Header string
}

RequestIDConfig controls RequestID middleware behavior.

Jump to

Keyboard shortcuts

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