bebo

package module
v0.0.0-...-20f29a4 Latest Latest
Warning

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

Go to latest
Published: Jan 14, 2026 License: MIT Imports: 33 Imported by: 0

README

bebo

bebo is a batteries-included Go framework focused on building REST APIs and server-rendered web apps with a lightweight custom router. Desktop support is available via Fyne.

Status

  • v0.1 release with custom router, middleware, JSON/HTML rendering, config defaults, static assets, and examples.
  • Desktop helpers live in desktop/ and depend on Fyne.

Requirements

  • Go 1.25 (as requested). If you are on a released Go toolchain, downgrade the go directive in go.mod.

Features

  • Custom router with params (/users/:id), wildcards (/assets/*path), groups, and host-based routing
  • Method-not-allowed handling
  • Named routes + path/query helpers
  • Middleware chain (request ID, recovery, logging, CORS, body limit, timeout, auth, rate limiting)
  • Rate limiting (in-memory + Redis token bucket) with per-route policies
  • Security headers, IP allow/deny, CSRF protection
  • Security helpers: CSP builder, secure cookies, rotating JWT keys
  • Cookie-based sessions + memory/redis/postgres stores
  • Redis cache adapter
  • Flash messages (session-backed) + CSRF template helpers
  • Method override for HTML forms (PUT/PATCH/DELETE)
  • Compression (gzip) + response ETag + cache control
  • JSON and HTML rendering with layouts, template funcs, partials, reload, and embedded templates
  • HTML error pages with configurable templates
  • Health/ready checks registry
  • Static file helper with cache headers + ETag (disk or fs.FS)
  • Metrics registry + JSON handler + histogram buckets + Prometheus exporter
  • Tracing hooks middleware + optional OpenTelemetry adapter
  • Observability: structured access logs (trace/span IDs, request/response bytes) + auth-gated pprof endpoints
  • Request metadata propagation helpers (traceparent/request IDs)
  • Realtime (SSE/WebSocket) helpers
  • OpenAPI builder + JSON handler
  • HTTP client utilities (timeouts, retries, backoff, circuit breaker)
  • Background job runner (in-process queue, retries/backoff, dead-letter hooks)
  • Form/multipart binding + file upload helpers
  • Config defaults + env overrides + JSON config loader + layered profiles (base/env/secrets) with validation
  • Validation helpers (including struct tags, non-string fields, and custom validators)
  • Extensibility registry + auth/cache/validation hooks
  • Optional integrations as submodules (redis/postgres/otel)
  • Graceful shutdown helpers
  • Minimal CLI generator + migration commands
  • DB helpers + SQL migrations runner

Quick Start (API)

package main

import (
    "net/http"

    "github.com/devmarvs/bebo"
    "github.com/devmarvs/bebo/middleware"
)

func main() {
    app := bebo.New()
    app.Use(middleware.RequestID(), middleware.Recover(), middleware.Logger())

    app.GET("/health", func(ctx *bebo.Context) error {
        return ctx.JSON(http.StatusOK, map[string]string{"status": "ok"})
    })

    _ = app.RunWithSignals()
}

Routing & Groups

api := app.Group("/api", middleware.RequestID())
v1 := api.Group("/v1")

v1.GET("/users/:id", handler)

// Or use version helper for /api/v1
app.Version("v1").GET("/health", handler)

Host-Based Routing

app.Route("GET", "/", handler, bebo.WithHost("example.com"))
app.Route("GET", "/", handler, bebo.WithHost("*.example.com"))

Named Routes

app.Route("GET", "/users/:id", handler, bebo.WithName("user.show"))
path, _ := app.Path("user.show", map[string]string{"id": "42"})
path, _ = app.PathWithQuery("user.show", map[string]string{"id": "42"}, map[string]string{"q": "test"})

OpenAPI

spec := openapi.New(openapi.Info{Title: "bebo app", Version: "v0.1"})
_ = app.AddOpenAPIRoutes(spec, bebo.WithOpenAPIIncludeUnnamed(false))

app.GET("/openapi.json", func(ctx *bebo.Context) error {
    openapi.Handler(spec.Document()).ServeHTTP(ctx.ResponseWriter, ctx.Request)
    return nil
})

Static Assets

app.Static("/static", "./public")
app.StaticFS("/static", staticFS)

Middleware Examples

app.Use(
    middleware.CORS(middleware.CORSOptions{AllowedOrigins: []string{"https://example.com"}}),
    middleware.BodyLimit(2<<20),
    middleware.Timeout(5*time.Second),
    middleware.SecurityHeaders(middleware.DefaultSecurityHeaders()),
    middleware.Gzip(0),
)

limiter := middleware.NewLimiter(5, 10)
app.GET("/reports", reportsHandler, middleware.RateLimit(limiter))

Middleware Options

logOpts := middleware.DefaultLoggerOptions()
logOpts.SkipPaths = []string{"/health"}
logOpts.SampleRate = 0.2
app.Use(middleware.LoggerWithOptions(logOpts))

metricsOpts := middleware.DefaultMetricsOptions(registry)
metricsOpts.SkipPaths = []string{"/metrics"}
app.Use(middleware.MetricsWithOptions(metricsOpts))

traceOpts := middleware.DefaultTraceOptions(tracer)
traceOpts.SkipPaths = []string{"/metrics"}
traceOpts.SampleRate = 0.2
app.Use(middleware.TraceWithOptions(traceOpts))

Rate Limiting (Redis + Policies)

redisLimiter := middleware.NewRedisLimiter(5, 10, middleware.RedisLimiterOptions{Address: "127.0.0.1:6379"})
policies, _ := middleware.NewRateLimitPolicies([]middleware.RateLimitPolicy{
    {
        Method: http.MethodGet,
        Path:   "/reports/:id",
        Allow: func(ctx *bebo.Context, key string) (bool, error) {
            return redisLimiter.Allow(ctx.Request.Context(), key)
        },
    },
})
app.Use(policies.Middleware())

Request Metadata Propagation

app.Use(middleware.RequestID(), middleware.RequestContext())

runner := tasks.New(tasks.DefaultOptions())
runner.Start(context.Background())

app.POST("/jobs", func(ctx *bebo.Context) error {
    runner.Enqueue(tasks.Job{
        Context: ctx.Request.Context(),
        Handler: func(jobCtx context.Context) error {
            logger := bebo.LoggerFromContext(jobCtx, slog.Default())
            logger.Info("job started")
            return nil
        },
    })
    return ctx.Text(http.StatusAccepted, "queued")
})

Use bebo.InjectRequestMetadata to copy headers to outgoing HTTP requests, or enable client propagation:

client := httpclient.NewClient(httpclient.ClientOptions{
    Timeout:           10 * time.Second,
    Retry:             httpclient.DefaultRetryOptions(),
    PropagateMetadata: true,
})
_ = client

CSRF

app.Use(middleware.CSRF(middleware.CSRFOptions{}))

app.POST("/submit", func(ctx *bebo.Context) error {
    token := middleware.CSRFToken(ctx)
    _ = token
    return ctx.Text(http.StatusOK, "ok")
})

CSP Builder

policy := security.NewCSP().
    DefaultSrc("'self'").
    ScriptSrc("'self'", "cdn.example.com").
    UpgradeInsecureRequests()
app.Use(middleware.SecurityHeaders(middleware.SecurityHeadersOptions{
    ContentSecurityPolicy: policy.String(),
}))

Secure Cookies

cookie := security.NewSecureCookie("session", "value", security.CookieOptions{})
http.SetCookie(ctx.ResponseWriter, cookie)

Method Override (HTML forms)

app.UsePre(middleware.MethodOverride(middleware.MethodOverrideOptions{}))

Forms can send a hidden _method field to trigger PUT/PATCH/DELETE.

IP Allow/Deny

filter, _ := middleware.IPFilter(middleware.IPFilterOptions{Allow: []string{"127.0.0.1"}})
app.Use(filter)

Sessions

cookieStore := session.NewCookieStore("bebo_session", []byte("secret"))
memoryStore := session.NewMemoryStore("bebo_session", 30*time.Minute)

app.GET("/profile", func(ctx *bebo.Context) error {
    sess, _ := cookieStore.Get(ctx.Request)
    sess.Set("user_id", "123")
    _ = sess.Save(ctx.ResponseWriter)
    return ctx.Text(http.StatusOK, "ok")
})

Persistent Sessions (Redis/Postgres)

redisStore := session.NewRedisStore(session.RedisOptions{
    Address: "127.0.0.1:6379",
    TTL:     30 * time.Minute,
})

pgStore, _ := session.NewPostgresStore(session.PostgresOptions{
    DB:   db,
    TTL:  30 * time.Minute,
    Table: "bebo_sessions",
})
_ = pgStore.EnsureTable(context.Background())

Note: Postgres requires a driver (pgx/pq) in your app.

Cache (Redis)

store := cache.NewRedisStore(cache.RedisOptions{
    Address:    "127.0.0.1:6379",
    DefaultTTL: 5 * time.Minute,
})
_ = store.Set(context.Background(), "user:1", []byte("cached"), 0)

Metrics

registry := metrics.New()
app.Use(middleware.Metrics(registry))

custom := metrics.NewWithBuckets([]time.Duration{5 * time.Millisecond, 25 * time.Millisecond, 100 * time.Millisecond, 500 * time.Millisecond, time.Second})
_ = custom

app.GET("/metrics", func(ctx *bebo.Context) error {
    metrics.PrometheusHandler(registry).ServeHTTP(ctx.ResponseWriter, ctx.Request)
    return nil
})

Use metrics.Handler(registry) for JSON snapshots.

Pprof (Authenticated)

authenticator := auth.JWTAuthenticator{Key: []byte("secret")}
_ = pprof.Register(app, authenticator)

Health & Readiness

registry := health.New(health.WithTimeout(500 * time.Millisecond))
registry.Add("db", func(ctx context.Context) error {
    return db.PingContext(ctx)
})
registry.AddReady("cache", func(ctx context.Context) error {
    return cache.Ping(ctx)
})

app.GET("/healthz", func(ctx *bebo.Context) error {
    registry.Handler().ServeHTTP(ctx.ResponseWriter, ctx.Request)
    return nil
})
app.GET("/readyz", func(ctx *bebo.Context) error {
    registry.ReadyHandler().ServeHTTP(ctx.ResponseWriter, ctx.Request)
    return nil
})

HTTP Client

breaker := httpclient.NewCircuitBreaker(httpclient.CircuitBreakerOptions{
    MaxFailures:  5,
    ResetTimeout: 30 * time.Second,
})

client := httpclient.NewClient(httpclient.ClientOptions{
    Timeout:           10 * time.Second,
    Retry:             httpclient.DefaultRetryOptions(),
    Breaker:           breaker,
    PropagateMetadata: true,
})

Background Jobs

runner := tasks.New(tasks.DefaultOptions())
runner.Start(context.Background())

_ = runner.Enqueue(tasks.Job{
    Name:    "sync-users",
    Context: context.Background(),
    Handler: func(ctx context.Context) error {
        return nil
    },
})

Realtime (SSE/WebSocket)

app.GET("/events", func(ctx *bebo.Context) error {
    stream, err := realtime.StartSSE(ctx, realtime.SSEOptions{})
    if err != nil {
        return err
    }
    return stream.Send(realtime.SSEMessage{Event: "status", Data: "ok"})
})

app.GET("/ws", func(ctx *bebo.Context) error {
    conn, err := realtime.Upgrade(ctx, realtime.WebSocketOptions{})
    if err != nil {
        return err
    }
    defer conn.Close()

    for {
        msg, err := conn.ReadText()
        if err != nil {
            return err
        }
        _ = conn.WriteText("echo: " + msg)
    }
})

OpenTelemetry (optional)

OpenTelemetry support lives behind the otel build tag. Add the OpenTelemetry SDK to your project and build with -tags otel.

tracer, _ := otel.NewTracer("bebo")
app.Use(middleware.Trace(tracer))

Auth Scaffolding

type TokenAuth struct{}

func (a TokenAuth) Authenticate(ctx *bebo.Context) (*bebo.Principal, error) {
    token := ctx.Request.Header.Get("Authorization")
    if token == "" {
        return nil, nil
    }
    return &bebo.Principal{ID: "user-1"}, nil
}

app.GET("/private", privateHandler, middleware.RequireAuth(TokenAuth{}))

JWT Auth

authenticator := auth.JWTAuthenticator{
    Key:      []byte("secret"),
    Issuer:   "bebo",
    Audience: "api",
}

app.GET("/private", privateHandler, middleware.RequireAuth(authenticator))

Rotating keys:

keys := auth.JWTKeySet{
    Primary: auth.JWTKey{ID: "v2", Secret: []byte("new")},
    Fallback: []auth.JWTKey{
        {ID: "v1", Secret: []byte("old")},
    },
}

// Use keys.Sign to mint tokens with the primary key.
_ = keys

rotating := auth.JWTAuthenticator{KeySet: &keys}
app.GET("/private", privateHandler, middleware.RequireAuth(rotating))

Request Binding

type Signup struct {
    Email string `form:"email"`
    Age   int    `form:"age"`
}

var form Signup
if err := ctx.BindForm(&form); err != nil {
    return err
}

file, _ := ctx.FormFile("avatar", bebo.DefaultMultipartMemory)
_ = ctx.SaveUploadedFile(file, "/tmp/"+file.Filename)

Web Templating

Templates live in a directory (default *.html). If LayoutTemplate is set, each page template should define "content" and the layout should template "content".

resolver := assets.NewResolver("./public")
app := bebo.New(
    bebo.WithTemplateReload(true),
    bebo.WithTemplateFuncs(render.FuncMap{
        "asset": resolver.Func(),
    }),
)

See the runnable example:

examples/web

Template Helpers (CSRF + Flash)

store := flash.New(session.NewCookieStore("bebo_session", []byte("secret")))

app := bebo.New(
    bebo.WithTemplateFuncs(web.Funcs()),
)
app.Use(middleware.CSRF(middleware.CSRFOptions{}))

app.GET("/", func(ctx *bebo.Context) error {
    view, err := web.TemplateDataFrom(ctx, &store, pageData)
    if err != nil {
        return err
    }
    return ctx.HTML(http.StatusOK, "home.html", view)
})

Template usage:

<form method="post">
  {{ csrfField .CSRFToken }}
</form>
<ul>
  {{ range .Flash }}
  <li class="flash-{{ .Type }}">{{ .Text }}</li>
  {{ end }}
</ul>

Template Partials

Enable nested templates and partial discovery. By default, files under partials/ or prefixed with _ are treated as partials when subdir loading is enabled.

app := bebo.New(
    bebo.WithTemplateSubdirs(true),
    bebo.WithTemplatePartials("partials/**/*.html", "_*.html"),
)

Embedded Templates & Assets

import (
    "embed"
    "io/fs"
)

//go:embed templates/* static/*
var embedded embed.FS

tmplFS, _ := fs.Sub(embedded, "templates")
staticFS, _ := fs.Sub(embedded, "static")

app := bebo.New(
    bebo.WithTemplateFS(tmplFS, "."),
    bebo.WithTemplateFSDevDir("templates"),
    bebo.WithTemplateReload(true),
)
app.StaticFS("/static", staticFS)

HTML Error Pages

If your error templates live in nested directories, enable bebo.WithTemplateSubdirs(true). Error templates receive ErrorPageData with a nested Error envelope and RequestID.

app := bebo.New(
    bebo.WithRenderer(engine),
    bebo.WithErrorTemplates(map[int]string{
        http.StatusNotFound:           "errors/404.html",
        http.StatusInternalServerError: "errors/500.html",
        0:                             "errors/default.html",
    }),
)

Desktop (Fyne)

The desktop package is optional but included:

examples/desktop

Helpers cover window icons, menus, and tray menus via desktop.WindowConfig. To package a desktop app with an icon:

fyne package -os darwin -icon path/to/icon.png

Fyne is declared in go.mod for the desktop helpers.

Configuration

Defaults are in config.Default() or bebo.DefaultConfig(). You can apply env overrides:

cfg := bebo.ConfigFromEnv("BEBO_", bebo.DefaultConfig())

Load JSON config layered with env:

cfg, err := bebo.LoadConfig("config.json", "BEBO_")

Load layered profiles (base + env + secrets) with validation:

profile := bebo.ConfigProfile{
    BasePath:    "config/base.json",
    EnvPath:     "config/production.json",
    SecretsPath: "config/secrets.json",
    EnvPrefix:   "BEBO_",
}
cfg, err := bebo.LoadConfigProfile(profile)

Typed loaders are available via config.Loader[T] for custom config structs. Env keys include: ADDRESS, READ_TIMEOUT, WRITE_TIMEOUT, TEMPLATES_DIR, LAYOUT_TEMPLATE, TEMPLATE_RELOAD.

Versioning

See VERSIONING.md, DEPRECATION.md, and CHANGELOG.md.

Docs

  • Security: SECURITY.md
  • Hardening: docs/hardening.md
  • Authentication defaults: docs/authentication.md
  • Secrets checklist: docs/secrets.md
  • Crypto/key rotation: docs/crypto-keys.md
  • Production runbook: docs/runbook.md
  • Scaling: docs/scaling.md
  • Timeouts & circuit breakers: docs/timeouts.md
  • Config profiles: docs/config-profiles.md
  • Extensibility: docs/extensibility.md
  • Integrations: docs/integrations.md
  • Project structure: docs/project-structure.md
  • Scaffolding upgrades: docs/scaffolding-upgrades.md
  • Testing: docs/testing.md
  • Migration guide: docs/migration-guide.md
  • Deployment examples: deploy/docker/Dockerfile, deploy/k8s/
  • CRUD app example: examples/crud (runbook: examples/crud/RUNBOOK.md)

CLI

bebo new ./myapp -module github.com/me/myapp -web -template -profile
bebo route add -method GET -path /users/:id -name user.show
bebo crud new users -dir handlers -package handlers -templates templates
bebo migrate new -dir ./migrations -name create_users
bebo migrate plan -dir ./migrations

Supports -api, -web, and -desktop scaffolds.

DB Helpers

helper := db.Helper{Timeout: 2 * time.Second}
_, _ = helper.Exec(context.Background(), dbConn, "SELECT 1")

repo := db.NewRepository(dbConn, 2*time.Second)
limit, offset := db.Pagination{Page: 1, Size: 25}.LimitOffset()
query, args, _ := db.Select("id", "name").From("users").Where("active = ?", true).Build()
_ = limit
_ = offset
_ = query
_ = args

logged := db.WithQueryHook(dbConn, func(ctx context.Context, q string, args []any, d time.Duration, err error) {})
_ = logged

Migrations

runner := migrate.New(db, "./migrations")
runner.Locker = migrate.AdvisoryLocker{ID: 42, Timeout: 5 * time.Second}
_, _ = runner.Up(context.Background())

Files use 0001_name.up.sql and 0001_name.down.sql.

Layout

  • app.go, context.go: core app and request context
  • router/: custom router implementation
  • middleware/: built-in middleware
  • render/: JSON and HTML rendering
  • assets/: cache-busting asset helper
  • compat/: backwards-compatibility tests
  • cache/: Redis cache adapter
  • redis/: shared Redis client
  • security/: CSP builder + secure cookies
  • web/: template helpers (csrf + flash)
  • config/: config defaults + env overrides + JSON loader + profiles
  • validate/: basic validation helpers
  • metrics/: request metrics + Prometheus exporter
  • pprof/: authenticated pprof endpoints
  • health/: health and readiness checks
  • session/: cookie-backed sessions + memory store
  • flash/: flash messages
  • auth/: JWT authenticator helper
  • openapi/: OpenAPI builder + handler
  • otel/: OpenTelemetry adapter (build tag)
  • httpclient/: HTTP client utilities (retry/backoff/breaker)
  • tasks/: background jobs runner
  • realtime/: SSE + WebSocket helpers
  • db/: database helpers
  • migrate/: SQL migration runner
  • desktop/: Fyne helpers
  • docs/: security, runbooks, and guides
  • deploy/: container and Kubernetes examples
  • integrations/: optional submodules (redis/postgres/otel)
  • examples/: API, web, desktop, and CRUD samples

Roadmap

See ROADMAP.md.

Documentation

Index

Constants

View Source
const (
	TraceparentHeader = "traceparent"
	TracestateHeader  = "tracestate"
)
View Source
const DefaultMultipartMemory int64 = 32 << 20
View Source
const RequestIDHeader = "X-Request-ID"

Variables

View Source
var (
	// ErrRegistryNil indicates the registry is nil.
	ErrRegistryNil = errors.New("registry is nil")
	// ErrRegistryNameRequired indicates a missing registry name.
	ErrRegistryNameRequired = errors.New("registry name is required")
	// ErrRegistryExists indicates a registry entry already exists.
	ErrRegistryExists = errors.New("registry entry already exists")
	// ErrRegistryNotFound indicates a registry entry was not found.
	ErrRegistryNotFound = errors.New("registry entry not found")
)

Functions

func ConfigFromEnv

func ConfigFromEnv(prefix string, base config.Config) config.Config

ConfigFromEnv applies environment overrides using the given prefix.

func ConfigFromFile

func ConfigFromFile(path string, base config.Config) (config.Config, error)

ConfigFromFile loads config from a JSON file onto the base config.

func DefaultConfig

func DefaultConfig() config.Config

DefaultConfig returns default config values.

func InjectRequestMetadata

func InjectRequestMetadata(r *http.Request, metadata RequestMetadata)

InjectRequestMetadata sets request metadata headers.

func InjectRequestMetadataIfMissing

func InjectRequestMetadataIfMissing(r *http.Request, metadata RequestMetadata)

InjectRequestMetadataIfMissing sets request metadata headers only when missing.

func LoadConfig

func LoadConfig(path, envPrefix string) (config.Config, error)

LoadConfig loads config from file and applies env overrides.

func LoadConfigProfile

func LoadConfigProfile(profile config.Profile) (config.Config, error)

LoadConfigProfile loads config from base/env/secrets profiles with validation.

func NewRequestID

func NewRequestID() string

NewRequestID generates a new request id.

func RequestIDFromHeader

func RequestIDFromHeader(r *http.Request) string

RequestIDFromHeader returns the request id from headers.

func SetPrincipal

func SetPrincipal(ctx *Context, principal *Principal)

SetPrincipal stores the principal in context storage.

func TraceIDs

func TraceIDs(traceparent string) (string, string, bool)

TraceIDs parses trace and span ids from traceparent.

func ValidateConfig

func ValidateConfig(cfg config.Config) error

ValidateConfig validates configuration values.

func WithRequestMetadata

func WithRequestMetadata(ctx context.Context, metadata RequestMetadata) context.Context

WithRequestMetadata stores metadata in a context.

Types

type App

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

App is the main framework entrypoint.

func New

func New(options ...Option) *App

New creates a new App with defaults.

func (*App) AddOpenAPIRoutes

func (a *App) AddOpenAPIRoutes(builder *openapi.Builder, options ...OpenAPIOption) error

AddOpenAPIRoutes derives OpenAPI operations from registered routes.

func (*App) AuthHooks

func (a *App) AuthHooks() AuthHooks

AuthHooks returns the configured auth hooks.

func (*App) DELETE

func (a *App) DELETE(path string, handler Handler, middleware ...Middleware)

DELETE registers a DELETE route.

func (*App) GET

func (a *App) GET(path string, handler Handler, middleware ...Middleware)

GET registers a GET route.

func (*App) Group

func (a *App) Group(prefix string, middleware ...Middleware) *Group

Group creates a new route group.

func (*App) HEAD

func (a *App) HEAD(path string, handler Handler, middleware ...Middleware)

func (*App) Handle

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

Handle registers a route for an arbitrary method.

func (*App) ListenAndServe

func (a *App) ListenAndServe() error

ListenAndServe starts the HTTP server using config values.

func (*App) ListenAndServeTLS

func (a *App) ListenAndServeTLS(certFile, keyFile string) error

ListenAndServeTLS starts the HTTPS server using config values.

func (*App) PATCH

func (a *App) PATCH(path string, handler Handler, middleware ...Middleware)

PATCH registers a PATCH route.

func (*App) POST

func (a *App) POST(path string, handler Handler, middleware ...Middleware)

POST registers a POST route.

func (*App) PUT

func (a *App) PUT(path string, handler Handler, middleware ...Middleware)

PUT registers a PUT route.

func (*App) Path

func (a *App) Path(name string, params map[string]string) (string, bool)

Path builds a URL path from a named route and params.

func (*App) PathWithQuery

func (a *App) PathWithQuery(name string, params map[string]string, query map[string]string) (string, bool)

PathWithQuery builds a URL path from a named route and attaches query params.

func (*App) Registry

func (a *App) Registry() *Registry

Registry returns the app registry.

func (*App) Route

func (a *App) Route(method, path string, handler Handler, options ...RouteOption)

Route registers a route with options.

func (*App) RouteInfo

func (a *App) RouteInfo(name string) (RouteInfo, bool)

RouteInfo returns metadata for a named route.

func (*App) Routes

func (a *App) Routes() []RouteInfo

Routes returns all named routes.

func (*App) RoutesAll

func (a *App) RoutesAll() []RouteInfo

RoutesAll returns metadata for all registered routes.

func (*App) Run

func (a *App) Run(ctx context.Context) error

Run starts the server and shuts down when the context is canceled.

func (*App) RunWithSignals

func (a *App) RunWithSignals() error

RunWithSignals starts the server and handles SIGINT/SIGTERM for shutdown.

func (*App) ServeHTTP

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

ServeHTTP implements http.Handler.

func (*App) SetAuthHooks

func (a *App) SetAuthHooks(hooks AuthHooks)

SetAuthHooks updates the auth hooks.

func (*App) ShutdownTimeout

func (a *App) ShutdownTimeout() time.Duration

ShutdownTimeout returns the configured graceful shutdown timeout.

func (*App) Static

func (a *App) Static(prefix, dir string, options ...StaticOption)

Static registers a static file route.

func (*App) StaticFS

func (a *App) StaticFS(prefix string, fsys fs.FS, options ...StaticOption)

StaticFS registers a static file route from an fs.FS.

func (*App) Use

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

Use registers global middleware.

func (*App) UsePre

func (a *App) UsePre(middleware ...PreMiddleware)

UsePre registers pre-routing middleware.

func (*App) Version

func (a *App) Version(version string, middleware ...Middleware) *Group

Version creates a versioned group under /api/{version}.

type AuthHooks

type AuthHooks struct {
	BeforeAuthenticate func(ctx *Context)
	AfterAuthenticate  func(ctx *Context, principal *Principal, err error)
}

AuthHooks provides hook points around authentication.

type Authenticator

type Authenticator interface {
	Authenticate(*Context) (*Principal, error)
}

Authenticator validates a request and returns a principal.

type AuthenticatorFactory

type AuthenticatorFactory func(config map[string]any) (Authenticator, error)

AuthenticatorFactory builds an authenticator using a config map.

type Authorizer

type Authorizer interface {
	Authorize(*Context, *Principal) error
}

Authorizer checks if a principal can access a resource.

type CacheFactory

type CacheFactory func(config map[string]any) (cache.Store, error)

CacheFactory builds a cache store using a config map.

type Config

type Config = config.Config

Config is the framework configuration.

type ConfigProfile

type ConfigProfile = config.Profile

ConfigProfile describes layered config sources.

type Context

type Context struct {
	ResponseWriter http.ResponseWriter
	Request        *http.Request
	Params         router.Params
	// contains filtered or unexported fields
}

Context holds request-specific data.

func NewContext

func NewContext(w http.ResponseWriter, r *http.Request, params router.Params, app *App) *Context

NewContext constructs a Context.

func (*Context) App

func (c *Context) App() *App

App returns the owning app instance when available.

func (*Context) AuthHooks

func (c *Context) AuthHooks() AuthHooks

AuthHooks returns configured auth hooks when available.

func (*Context) BindForm

func (c *Context) BindForm(dst any) error

BindForm binds URL-encoded form values into dst.

func (*Context) BindJSON

func (c *Context) BindJSON(dst any) error

BindJSON binds the request body to a struct.

func (*Context) BindMultipart

func (c *Context) BindMultipart(dst any, maxMemory int64) error

BindMultipart binds multipart form values into dst.

func (*Context) FormFile

func (c *Context) FormFile(name string, maxMemory int64) (*multipart.FileHeader, error)

FormFile returns a file header from a multipart request.

func (*Context) Get

func (c *Context) Get(key string) (any, bool)

Get retrieves a stored value.

func (*Context) HTML

func (c *Context) HTML(status int, name string, data any) error

HTML renders a template.

func (*Context) JSON

func (c *Context) JSON(status int, payload any) error

JSON responds with JSON.

func (*Context) Logger

func (c *Context) Logger() Logger

Logger returns the app logger.

func (*Context) Param

func (c *Context) Param(name string) string

Param returns a route param.

func (*Context) ParamBool

func (c *Context) ParamBool(name string) (bool, error)

ParamBool returns a route param as a bool.

func (*Context) ParamInt

func (c *Context) ParamInt(name string) (int, error)

ParamInt returns a route param as an int.

func (*Context) ParamInt64

func (c *Context) ParamInt64(name string) (int64, error)

ParamInt64 returns a route param as an int64.

func (*Context) Query

func (c *Context) Query(name string) string

Query returns a query param.

func (*Context) QueryBool

func (c *Context) QueryBool(name string) (bool, error)

QueryBool returns a query param as a bool.

func (*Context) QueryInt

func (c *Context) QueryInt(name string) (int, error)

QueryInt returns a query param as an int.

func (*Context) QueryInt64

func (c *Context) QueryInt64(name string) (int64, error)

QueryInt64 returns a query param as an int64.

func (*Context) Render

func (c *Context) Render(status int, fn render.RenderFunc) error

Render uses a custom render function.

func (*Context) RequestID

func (c *Context) RequestID() string

RequestID returns the request id header.

func (*Context) SaveUploadedFile

func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error

SaveUploadedFile writes a multipart file to disk.

func (*Context) Set

func (c *Context) Set(key string, value any)

Set stores a value in the context.

func (*Context) Text

func (c *Context) Text(status int, message string) error

Text responds with plain text.

type ErrorEnvelope

type ErrorEnvelope struct {
	Code      string
	Message   string
	Fields    []validate.FieldError
	RequestID string
}

ErrorEnvelope describes a standardized error payload.

type ErrorHandler

type ErrorHandler func(*Context, error)

ErrorHandler processes errors returned by handlers.

type ErrorPageData

type ErrorPageData struct {
	Status    int
	Code      string
	Message   string
	Fields    []validate.FieldError
	RequestID string
	Error     ErrorEnvelope
}

ErrorPageData is passed to error templates.

type Group

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

Group defines a route group with a common prefix and middleware.

func (*Group) DELETE

func (g *Group) DELETE(path string, handler Handler, middleware ...Middleware)

DELETE registers a DELETE route in the group.

func (*Group) GET

func (g *Group) GET(path string, handler Handler, middleware ...Middleware)

GET registers a GET route in the group.

func (*Group) Group

func (g *Group) Group(prefix string, middleware ...Middleware) *Group

Group creates a nested group.

func (*Group) HEAD

func (g *Group) HEAD(path string, handler Handler, middleware ...Middleware)

func (*Group) Handle

func (g *Group) Handle(method, path string, handler Handler, middleware ...Middleware)

Handle registers a route in the group for an arbitrary method.

func (*Group) PATCH

func (g *Group) PATCH(path string, handler Handler, middleware ...Middleware)

PATCH registers a PATCH route in the group.

func (*Group) POST

func (g *Group) POST(path string, handler Handler, middleware ...Middleware)

POST registers a POST route in the group.

func (*Group) PUT

func (g *Group) PUT(path string, handler Handler, middleware ...Middleware)

PUT registers a PUT route in the group.

func (*Group) Route

func (g *Group) Route(method, path string, handler Handler, options ...RouteOption)

Route registers a route with options in the group.

type Handler

type Handler func(*Context) error

Handler handles a request and returns an error for centralized handling.

func TimeoutHandler

func TimeoutHandler(next Handler, duration time.Duration) Handler

TimeoutHandler wraps a handler with a timeout.

type Logger

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

Logger wraps slog.Logger with request context.

func LoggerFromContext

func LoggerFromContext(ctx context.Context, logger *slog.Logger) Logger

LoggerFromContext builds a logger that includes request metadata.

func LoggerFromRequest

func LoggerFromRequest(r *http.Request, logger *slog.Logger) Logger

LoggerFromRequest builds a logger using request metadata.

func (Logger) Debug

func (l Logger) Debug(msg string, attrs ...slog.Attr)

Debug logs a debug message.

func (Logger) Error

func (l Logger) Error(msg string, attrs ...slog.Attr)

Error logs an error message.

func (Logger) Info

func (l Logger) Info(msg string, attrs ...slog.Attr)

Info logs an info message.

func (Logger) Warn

func (l Logger) Warn(msg string, attrs ...slog.Attr)

Warn logs a warning message.

type Middleware

type Middleware func(Handler) Handler

Middleware wraps a handler with additional behavior.

type MiddlewareFactory

type MiddlewareFactory func(config map[string]any) (Middleware, error)

MiddlewareFactory builds middleware using a config map.

type OpenAPIOption

type OpenAPIOption func(*OpenAPIOptions)

OpenAPIOption customizes OpenAPI route derivation.

func WithOpenAPIIncludeUnnamed

func WithOpenAPIIncludeUnnamed(enabled bool) OpenAPIOption

WithOpenAPIIncludeUnnamed toggles inclusion of unnamed routes.

func WithOpenAPISkipMethods

func WithOpenAPISkipMethods(methods ...string) OpenAPIOption

WithOpenAPISkipMethods skips specific HTTP methods.

func WithOpenAPISkipPaths

func WithOpenAPISkipPaths(paths ...string) OpenAPIOption

WithOpenAPISkipPaths skips paths that match the provided patterns.

func WithOpenAPITagFromHost

func WithOpenAPITagFromHost(enabled bool) OpenAPIOption

WithOpenAPITagFromHost applies host values as OpenAPI tags.

type OpenAPIOptions

type OpenAPIOptions struct {
	IncludeUnnamed bool
	SkipPaths      []string
	SkipMethods    []string
	TagFromHost    bool
}

OpenAPIOptions configures automatic OpenAPI route derivation.

func DefaultOpenAPIOptions

func DefaultOpenAPIOptions() OpenAPIOptions

DefaultOpenAPIOptions returns the default OpenAPI options.

type Option

type Option func(*App)

Option customizes the app instance.

func WithAuthHooks

func WithAuthHooks(hooks AuthHooks) Option

WithAuthHooks configures authentication hooks.

func WithConfig

func WithConfig(cfg config.Config) Option

WithConfig overrides the default config.

func WithErrorHandler

func WithErrorHandler(handler ErrorHandler) Option

WithErrorHandler overrides the default error handler.

func WithErrorTemplates

func WithErrorTemplates(templates map[int]string) Option

WithErrorTemplates configures template names for HTML error pages. Use status code keys, or 0 for a default template.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger uses a custom logger.

func WithRegistry

func WithRegistry(registry *Registry) Option

WithRegistry sets a custom registry for extensibility.

func WithRenderer

func WithRenderer(engine *render.Engine) Option

WithRenderer sets a custom template engine.

func WithTemplateFS

func WithTemplateFS(fsys fs.FS, dir string) Option

WithTemplateFS configures embedded templates from an fs.FS.

func WithTemplateFSDevDir

func WithTemplateFSDevDir(dir string) Option

WithTemplateFSDevDir sets a disk path for template reloads when using embedded templates.

func WithTemplateFuncs

func WithTemplateFuncs(funcs render.FuncMap) Option

WithTemplateFuncs registers template functions for the built-in renderer.

func WithTemplatePartials

func WithTemplatePartials(patterns ...string) Option

WithTemplatePartials configures glob patterns for partial templates.

func WithTemplateReload

func WithTemplateReload(enabled bool) Option

WithTemplateReload enables template reloading for development.

func WithTemplateSubdirs

func WithTemplateSubdirs(enabled bool) Option

WithTemplateSubdirs enables nested template directories.

type Params

type Params = router.Params

Params is an alias for router.Params.

type Plugin

type Plugin interface {
	Name() string
	Register(*Registry) error
}

Plugin registers components with a registry.

type PreMiddleware

type PreMiddleware func(*Context) error

PreMiddleware runs before routing.

type Principal

type Principal struct {
	ID     string
	Roles  []string
	Claims map[string]any
}

Principal represents an authenticated actor.

func PrincipalFromContext

func PrincipalFromContext(ctx *Context) (*Principal, bool)

PrincipalFromContext extracts the principal from context storage.

type Registry

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

Registry stores registered middleware, auth, cache, and validators.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty registry.

func (*Registry) Authenticator

func (r *Registry) Authenticator(name string, config map[string]any) (Authenticator, error)

Authenticator builds a named authenticator.

func (*Registry) Cache

func (r *Registry) Cache(name string, config map[string]any) (cache.Store, error)

Cache builds a named cache store.

func (*Registry) Middleware

func (r *Registry) Middleware(name string, config map[string]any) (Middleware, error)

Middleware builds a named middleware.

func (*Registry) RegisterAuthenticator

func (r *Registry) RegisterAuthenticator(name string, factory AuthenticatorFactory) error

RegisterAuthenticator registers a named authenticator factory.

func (*Registry) RegisterCache

func (r *Registry) RegisterCache(name string, factory CacheFactory) error

RegisterCache registers a named cache store factory.

func (*Registry) RegisterMiddleware

func (r *Registry) RegisterMiddleware(name string, factory MiddlewareFactory) error

RegisterMiddleware registers a named middleware factory.

func (*Registry) RegisterValidator

func (r *Registry) RegisterValidator(name string, fn validate.ValidatorFunc) error

RegisterValidator registers a named validator and exposes it via validate.Register.

func (*Registry) Use

func (r *Registry) Use(plugin Plugin) error

Use registers a plugin and its components.

func (*Registry) Validator

func (r *Registry) Validator(name string) (validate.ValidatorFunc, error)

Validator returns a named validator if registered.

type RequestMetadata

type RequestMetadata struct {
	RequestID   string
	Traceparent string
	Tracestate  string
}

RequestMetadata holds request-scoped identifiers.

func RequestMetadataFromContext

func RequestMetadataFromContext(ctx context.Context) RequestMetadata

RequestMetadataFromContext returns metadata stored in a context.

func RequestMetadataFromRequest

func RequestMetadataFromRequest(r *http.Request) RequestMetadata

RequestMetadataFromRequest returns metadata from the request and context.

type RouteInfo

type RouteInfo struct {
	Name    string
	Method  string
	Host    string
	Pattern string
}

RouteInfo describes a named route.

type RouteOption

type RouteOption func(*routeConfig)

RouteOption customizes route registration.

func WithHost

func WithHost(host string) RouteOption

WithHost scopes a route to a host or wildcard (e.g. "api.example.com", "*.example.com").

func WithMiddleware

func WithMiddleware(middleware ...Middleware) RouteOption

WithMiddleware attaches middleware to a route.

func WithName

func WithName(name string) RouteOption

WithName assigns a name to a route.

func WithTimeout

func WithTimeout(timeout time.Duration) RouteOption

WithTimeout sets a per-route timeout.

type StaticOption

type StaticOption func(*staticConfig)

StaticOption configures static file handling.

func StaticCacheControl

func StaticCacheControl(value string) StaticOption

StaticCacheControl sets the Cache-Control header value.

func StaticETag

func StaticETag(enabled bool) StaticOption

StaticETag enables ETag handling.

func StaticIndex

func StaticIndex(name string) StaticOption

StaticIndex sets the index file name to serve for directories.

Directories

Path Synopsis
cmd
bebo command
examples
api command
crud command
desktop command
web command
internal

Jump to

Keyboard shortcuts

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