burrow

package module
v0.13.0 Latest Latest
Warning

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

Go to latest
Published: Apr 15, 2026 License: MIT Imports: 44 Imported by: 0

README

Burrow

CI Release Go Version Go Report Card License Docs

A web framework for Go developers who want something like Django, Rails, or Flask — but with the deployment simplicity of a single static binary.

Most Go web development follows the "API backend + SPA frontend" pattern. Burrow takes a different approach: server-rendered HTML with templates, modular apps with their own routes and middleware, and a document database that just works. Deploy with embedded SQLite as a single binary, or connect to PostgreSQL for scale — same code, same API.

Built on Chi, Den (ODM with SQLite and PostgreSQL backends), and Go's standard html/template. Ideal for self-hosted applications, internal tools, or any project where "download, start, use" is the goal.

[!TIP] Why Burrow? A burrow is a network of interconnected chambers — each with its own purpose, yet part of a larger whole. That's exactly how the framework works: pluggable apps are the rooms, and your gophers live in them.

[!NOTE] Burrow is designed for server-rendered web applications, not API-only services. If you're building a JSON API with a separate frontend, a lighter router like Chi on its own is probably a better fit.

Features

  • App-based architecture — build your application from composable, self-contained apps
  • SQLite or PostgreSQL — embedded SQLite (pure Go, no CGO) for single-binary deploys, or PostgreSQL for scale — switch with one flag
  • Automatic schema — document types declared in code, tables and indexes created on startup
  • Standard templates — Go's html/template with a global template set, per-app FuncMaps, and automatic layout wrapping
  • CSS-agnostic — bring your own CSS framework (Bootstrap, Tailwind, etc.)
  • Layout system — app layout via server, admin layout via admin package
  • CLI configuration — flags, environment variables, and TOML config via urfave/cli
  • CSRF protection — automatic token generation and validation
  • Flash messages — session-based flash message system
  • Bootstrap integration — Bootstrap 5 CSS/JS, inline SVG icons, htmx, and dark mode theme switcher
  • Contrib apps — auth (WebAuthn/passkeys), sessions, i18n, admin, CSRF, flash messages, jobs, uploads, rate limiting, healthcheck, static files

Quick Start

mkdir myapp && cd myapp
go mod init myapp
go get github.com/oliverandrich/burrow@latest
package main

import (
    "context"
    "log"
    "net/http"
    "os"

    "github.com/oliverandrich/burrow"
    "github.com/go-chi/chi/v5"
    "github.com/urfave/cli/v3"
)

// homeApp is a minimal app with a single route.
type homeApp struct{}

func (a *homeApp) Name() string { return "home" }
func (a *homeApp) Routes(r chi.Router) {
    r.Method("GET", "/", burrow.Handle(func(w http.ResponseWriter, r *http.Request) error {
        return burrow.Text(w, http.StatusOK, "Hello from Burrow!")
    }))
}

func main() {
    srv := burrow.NewServer(
        &homeApp{},
    )

    cmd := &cli.Command{
        Name:   "myapp",
        Flags:  srv.Flags(nil),
        Action: srv.Run,
    }

    if err := cmd.Run(context.Background(), os.Args); err != nil {
        log.Fatal(err)
    }
}
go mod tidy
go run .

See example/hello/ for a minimal hello world app, or example/notes/ for a complete example with auth, admin, i18n, and more.

Architecture

contrib/        Reusable apps
  admin/        Admin panel coordinator + ModelAdmin
  auth/         WebAuthn passkeys, recovery codes, email verification
  authmail/     Pluggable email renderer + SMTP implementation
  bootstrap/    Bootstrap 5 CSS/JS/htmx assets, theme switcher, layout
  bsicons/      Bootstrap Icons as inline SVG template functions
  csrf/         CSRF protection
  healthcheck/  Liveness and readiness probes
  htmx/         htmx static asset + request/response helpers
  i18n/         Locale detection and translations
  jobs/         SQLite-backed background job queue
  messages/     Flash messages
  ratelimit/    Per-client rate limiting
  session/      Cookie-based sessions
  staticfiles/  Static file serving with content-hashed URLs
  uploads/      File upload storage and serving
example/        Example applications (hello world, notes app)
The App Interface

Every app implements burrow.App:

type App interface {
    Name() string
}

Apps can optionally implement additional interfaces:

Interface Purpose
HasDocuments Register Den document types
HasRoutes Register HTTP routes
HasMiddleware Contribute middleware
HasNavItems Contribute navigation items
HasTemplates Contribute .html template files
HasFuncMap Contribute static template functions
HasRequestFuncMap Contribute request-scoped template functions
Configurable Define CLI flags and read configuration
HasCLICommands Contribute CLI subcommands
Seedable Seed the database with initial data
HasAdmin Contribute admin panel routes and nav items
HasStaticFiles Contribute embedded static file assets
HasTranslations Contribute translation files
HasDependencies Declare required apps
HasJobs Register background job handlers
PostConfigurable Second-pass configuration after all apps are configured
HasShutdown Clean up on graceful shutdown
Layouts

Layouts are template name strings. The app layout wraps user-facing pages:

srv.SetLayout("myapp/layout")

The admin layout is owned by the admin package:

admin.New(admin.WithLayout("custom/admin-layout"))

RenderTemplate renders page content, then wraps it in the layout template with .Content set to the rendered fragment. Layout templates access dynamic data via template functions:

{{ range navLinks }}...{{ end }}  {{/* filtered nav with active state */}}
{{ if currentUser }}...{{ end }}  {{/* authenticated user */}}
{{ csrfToken }}                   {{/* CSRF token for forms */}}
Configuration

Configuration is resolved in order: CLI flags > environment variables > TOML file.

Core flags include --host, --port, --database-dsn, --log-level, --log-format, --tls-mode, and more. Apps can contribute their own flags via the Configurable interface.

Document Registration

Apps declare their document types by implementing HasDocuments:

func (a *App) Documents() []any {
    return []any{&Poll{}, &Choice{}}
}

Tables and indexes are created automatically on startup via Den's Register(). No SQL migration files needed.

Development

just setup          # Check that all required dev tools are installed
just test           # Run all tests
just lint           # Run golangci-lint
just fmt            # Format code
just coverage       # Generate coverage report
just tidy           # Tidy module dependencies
just example-hello  # Run the hello world example
just example-notes  # Run the notes example application

Requires Go 1.25+. Run just setup to verify your dev environment.

Documentation

Full documentation is available in the docs/ directory.

License

Burrow is licensed under the MIT License.

The Go Gopher was originally designed by Renee French and is licensed under CC BY 4.0.

Third-party licenses are listed in THIRD_PARTY_LICENSES.md.

Documentation

Overview

Package burrow is a Go web framework built on chi, Den/SQLite, and html/template. It provides a modular architecture where features are packaged as "apps" that plug into a shared server.

Getting Started

Create a server, register apps, and run:

srv := burrow.NewServer(
    session.New(),
    csrf.New(),
    myapp.New(),
)
srv.SetLayout(myLayout)

app := &cli.Command{
    Name:   "mysite",
    Flags:  srv.Flags(nil),
    Action: srv.Run,
}
_ = app.Run(context.Background(), os.Args)

NewServer sorts apps by declared dependencies automatically. The boot sequence runs migrations, configures each app from CLI/ENV/TOML flags, and starts the HTTP server with graceful shutdown.

Handler Functions

Burrow handlers return an error instead of silently swallowing failures:

func listItems(w http.ResponseWriter, r *http.Request) error {
    items, err := fetchItems(r.Context())
    if err != nil {
        return err // logged and rendered as 500
    }
    return burrow.JSON(w, http.StatusOK, items)
}

Wrap them with Handle to get a standard http.HandlerFunc:

r.Get("/items", burrow.Handle(listItems))

Return an HTTPError to control the status code and message:

return burrow.NewHTTPError(http.StatusNotFound, "item not found")

Response Helpers

JSON, Text, and HTML write responses with correct Content-Type headers. Render writes pre-rendered template.HTML (useful for HTMX fragments). RenderTemplate executes a named template and wraps it in the layout for full-page requests, or returns the fragment alone for HTMX requests.

Request Binding and Validation

Bind parses a request body (JSON, multipart, or form-encoded) into a struct and validates it using "validate" struct tags. On validation failure it returns a *ValidationError containing per-field errors:

type CreateItem struct {
    Name  string `form:"name"  validate:"required"`
    Email string `form:"email" validate:"required,email"`
}

func create(w http.ResponseWriter, r *http.Request) error {
    var input CreateItem
    if err := burrow.Bind(r, &input); err != nil {
        var ve *burrow.ValidationError
        if errors.As(err, &ve) {
            return burrow.JSON(w, 422, ve.Errors)
        }
        return err
    }
    // input is valid
}

Validate can be called standalone on any struct.

App Interface

Every app implements App (Name only). Apps gain additional capabilities by implementing optional interfaces:

Templates

Apps contribute .html files via HasTemplates. All templates are parsed into a single global html/template.Template at boot time. Templates use {{ define "appname/templatename" }} blocks for namespacing. Apps can add template functions via HasFuncMap (static) and HasRequestFuncMap (per-request, e.g. for CSRF tokens or the current user).

Pagination

ParsePageRequest extracts limit and page from the query string. Use QuerySet.Limit().Skip() + OffsetResult for offset-based pagination. PageResponse wraps items and pagination metadata for JSON APIs.

Contrib Apps

The contrib/ directory provides reusable apps:

  • auth — WebAuthn passkey authentication with recovery codes
  • authmail — pluggable email rendering with SMTP backend
  • session — cookie-based sessions (gorilla/sessions)
  • csrf — CSRF protection (gorilla/csrf)
  • i18n — locale detection and translations (go-i18n)
  • admin — admin panel with generic CRUD via ModelAdmin
  • bootstrap — Bootstrap 5 CSS/JS with dark mode
  • bsicons — Bootstrap Icons as inline SVG template functions
  • alpine — Alpine.js asset serving via staticfiles
  • htmx — HTMX asset serving and request/response helpers
  • jobs — SQLite-backed in-process job queue with retry
  • sse — Server-Sent Events with in-memory pub/sub broker
  • uploads — pluggable file upload storage
  • messages — flash messages via session storage
  • ratelimit — per-client token bucket rate limiting
  • healthcheck — liveness and readiness probes
  • staticfiles — static file serving with content-hashed URLs

Index

Constants

This section is empty.

Variables

View Source
var ErrNoTemplateExecutor = errors.New("burrow: no template executor in context")

ErrNoTemplateExecutor is returned when Render or RenderFragment is called without a TemplateExecutor in the context.

Functions

func Bind

func Bind(r *http.Request, v any) error

Bind parses the request body into the given struct and validates it.

Content-Type dispatch:

  • application/json → JSON decoding
  • multipart/form-data → multipart parsing + form decoding
  • everything else → form-encoded parsing + form decoding

Form decoding uses "form" struct tags (falling back to "json", then field name) and supports all basic types (string, int, bool, float, slices, etc.).

After decoding, Bind calls Validate automatically. If validation fails it returns a *ValidationError with per-field errors. Structs without "validate" tags pass through unchanged.

func CaptureResult added in v0.13.0

func CaptureResult(ctx context.Context, data []byte)

CaptureResult writes a result into the context's ResultCapture. It is a no-op if no capture is present in the context.

func ContextValue

func ContextValue[T any](ctx context.Context, key any) (T, bool)

ContextValue retrieves a typed value from the context. It is the generic counterpart to WithContextValue, used by contrib app authors to read back app-specific context values with type safety.

func CoreFlags

func CoreFlags(configSource func(key string) cli.ValueSource) []cli.Flag

CoreFlags returns the CLI flags for core framework configuration. If configSource is provided, it is used as an additional value source (e.g. a TOML file sourcer) for each flag.

func FlagSources

func FlagSources(configSource func(key string) cli.ValueSource, envVar, tomlKey string) cli.ValueSourceChain

FlagSources builds a cli.ValueSourceChain from an environment variable and an optional TOML key. If configSource is nil, only the env var is used. This is the standard way for contrib apps to wire up flag sources:

src := burrow.FlagSources(configSource, "MY_ENV_VAR", "app.toml_key")

func HTML

func HTML(w http.ResponseWriter, code int, s string) error

HTML writes an HTML response with the given status code.

func Handle

func Handle(fn HandlerFunc) http.HandlerFunc

Handle converts a HandlerFunc into a standard http.HandlerFunc with centralized error handling.

func IsLocalhost

func IsLocalhost(host string) bool

IsLocalhost checks if the host is a localhost address.

func JSON

func JSON(w http.ResponseWriter, code int, v any) error

JSON writes a JSON response with the given status code.

func Layout

func Layout(ctx context.Context) string

Layout retrieves the layout template name from the context.

func OpenDB added in v0.7.0

func OpenDB(dsn string) (*den.DB, error)

OpenDB opens a database from a URL-style DSN with struct-tag validation enabled by default. Documents with `validate:"..."` tags will be checked before every Insert and Update; a violation returns an error wrapping den.ErrValidation.

Supported schemes:

  • sqlite:///path/to/db — SQLite file database
  • postgres://user:pass@host:5432/db — PostgreSQL

For the rare case where you need the pre-v0.12.0 behavior (no tag validation at the data layer), use OpenDBWithoutValidation.

func OpenDBWithoutValidation added in v0.12.0

func OpenDBWithoutValidation(dsn string) (*den.DB, error)

OpenDBWithoutValidation opens a database with struct-tag validation DISABLED. This is an escape hatch for migration scenarios where an existing project has `validate:"..."` tags on documents that previously had no effect, and fixing all the data violations in one pass is not practical.

New projects should use OpenDB. Prefer cleaning up the validation tags and switching back to OpenDB as soon as possible.

func PageNumbers added in v0.6.0

func PageNumbers(current, total int) []int

PageNumbers returns page numbers to display for pagination, using -1 for ellipsis gaps. Shows at most 7 slots: first, last, current, and neighbors with ellipsis.

func PageURL added in v0.6.0

func PageURL(basePath, rawQuery string, page int) string

PageURL builds a URL that preserves existing query parameters from rawQuery and sets (or replaces) the "page" parameter. This is useful for pagination links that need to retain search terms, filters, and other query state.

burrow.PageURL("/admin/notes", "q=alpha&sort=-created_at", 3)
// => "/admin/notes?page=3&q=alpha&sort=-created_at"

func Render

func Render(w http.ResponseWriter, r *http.Request, statusCode int, name string, data map[string]any) error

Render executes a named template and writes the result. It applies automatic layout/HTMX logic:

  • HTMX request (HX-Request header) → fragment only, no layout
  • Normal request + layout name in context → fragment wrapped in layout
  • Normal request + no layout → fragment only

func RenderContent added in v0.6.0

func RenderContent(w http.ResponseWriter, r *http.Request, statusCode int, content template.HTML, data map[string]any) error

RenderContent writes pre-rendered HTML content, applying the same layout and HTMX logic as Render. The data map is passed to the layout template with "Content" added automatically.

This is useful when content was rendered by a separate template system (e.g., a custom renderer's templates) but still needs layout wrapping.

func RenderError added in v0.5.0

func RenderError(w http.ResponseWriter, r *http.Request, code int, message string)

RenderError writes an error response. For JSON API requests (Accept: application/json) it returns a JSON object. Otherwise it renders the "error/{code}" template through the standard Render pipeline (with layout wrapping, HTMX support, etc.).

func RenderFragment added in v0.9.0

func RenderFragment(ctx context.Context, name string, data map[string]any) (template.HTML, error)

RenderFragment renders a named template outside of an HTTP request. It retrieves the TemplateExecutor from ctx, so the context must have been enriched with WithTemplateExecutor (plus any request-scoped values the template needs, e.g., i18n.WithLocale).

Use this for background jobs, SSE broadcasts, CLI commands, or any non-HTTP code that needs to render templates.

func RenderTemplate deprecated

func RenderTemplate(w http.ResponseWriter, r *http.Request, statusCode int, name string, data map[string]any) error

Deprecated: Use Render instead.

func RequestPath added in v0.9.0

func RequestPath(ctx context.Context) string

RequestPath retrieves the request path from the context. Returns an empty string if no path is set.

func TestDB added in v0.5.0

func TestDB(t *testing.T) *den.DB

TestDB returns a file-backed SQLite database wrapped in a den.DB for testing. Struct-tag validation is enabled by default to match OpenDB. The database is created in testing.T.TempDir and closed automatically when the test finishes.

func TestErrorExecContext added in v0.5.0

func TestErrorExecContext(ctx context.Context) context.Context

TestErrorExecContext returns a context with a minimal TemplateExecutor that renders error templates as "<code>: <message>". Use this in tests that trigger error responses through Handle or RenderError.

func TestErrorExecMiddleware added in v0.5.0

func TestErrorExecMiddleware(next http.Handler) http.Handler

TestErrorExecMiddleware is an HTTP middleware that injects TestErrorExecContext into the request context. Use this in tests that need error rendering support.

func Text

func Text(w http.ResponseWriter, code int, s string) error

Text writes a plain text response with the given status code.

func Validate

func Validate(v any) error

Validate validates a struct using "validate" struct tags. Returns nil if v is not a struct, has no validate tags, or passes all checks.

func WithAuthChecker added in v0.4.0

func WithAuthChecker(ctx context.Context, checker AuthChecker) context.Context

WithAuthChecker stores an AuthChecker in the context. This is typically called by auth middleware to make authentication state available to the core template functions without an import cycle.

func WithContextValue

func WithContextValue(ctx context.Context, key, val any) context.Context

WithContextValue returns a new context with the given key-value pair. This is a convenience wrapper around context.WithValue used primarily by contrib app authors to store app-specific values in the request context. Application developers typically use typed helpers like WithLayout or contrib-specific functions (e.g. csrf.WithToken) instead.

func WithLayout

func WithLayout(ctx context.Context, name string) context.Context

WithLayout stores the layout template name in the context.

func WithNavItems

func WithNavItems(ctx context.Context, items []NavItem) context.Context

WithNavItems stores navigation items in the context.

func WithRequestPath added in v0.9.0

func WithRequestPath(ctx context.Context, path string) context.Context

WithRequestPath stores the request path in the context. This is set automatically by the template middleware for HTTP requests. For non-HTTP rendering (background jobs, SSE, CLI), callers should set it explicitly if nav-link highlighting is needed.

func WithResultCapture added in v0.13.0

func WithResultCapture(ctx context.Context, rc *ResultCapture) context.Context

WithResultCapture returns a context carrying the given ResultCapture.

func WithTemplateExecutor

func WithTemplateExecutor(ctx context.Context, exec TemplateExecutor) context.Context

WithTemplateExecutor stores the template executor in the context.

Types

type AdminAuth added in v0.13.0

type AdminAuth interface {
	RequireAuth() func(http.Handler) http.Handler
	RequireAdmin() func(http.Handler) http.Handler
}

AdminAuth provides authentication and authorization middleware for the admin panel. The admin app discovers an AdminAuth provider from the registry during Configure and uses its middleware to protect /admin routes. contrib/auth implements this interface.

type App

type App interface {
	Name() string
}

App is the required interface that all apps must implement. An app has a unique name used for identification in the registry, migrations, and logging.

type AppConfig

type AppConfig struct {
	DB         *den.DB
	Registry   *Registry
	Config     *Config
	WithLocale func(ctx context.Context, lang string) context.Context
	// contains filtered or unexported fields
}

AppConfig is passed to each app's Configure method, providing access to shared framework resources.

func (*AppConfig) IconFuncs added in v0.6.0

func (cfg *AppConfig) IconFuncs() map[string]IconFunc

IconFuncs returns all registered icon functions.

func (*AppConfig) RegisterIconFunc added in v0.6.0

func (cfg *AppConfig) RegisterIconFunc(name string, fn IconFunc)

RegisterIconFunc registers an icon function for use in templates. Duplicate registrations of the same name are silently ignored, allowing multiple apps to depend on the same icon.

type AuthChecker added in v0.4.0

type AuthChecker struct {
	IsAuthenticated func() bool
	IsAdmin         func() bool
}

AuthChecker provides authentication and authorization checks via closures. This allows the core framework to filter nav items by auth state without importing contrib/auth. Auth apps inject an AuthChecker into the context; the framework reads it when building NavLinks.

type Config

type Config struct {
	TLS      TLSConfig
	Database DatabaseConfig
	Server   ServerConfig
	I18n     I18nConfig
}

Config holds core framework configuration.

func NewConfig

func NewConfig(cmd *cli.Command) *Config

NewConfig creates a Config from a parsed CLI command.

func (*Config) IsHTTPS added in v0.13.0

func (c *Config) IsHTTPS() bool

IsHTTPS reports whether the base URL uses HTTPS.

func (*Config) ResolveBaseURL

func (c *Config) ResolveBaseURL() string

ResolveBaseURL computes the base URL from server and TLS config if BaseURL is not explicitly set.

func (*Config) ValidateTLS

func (c *Config) ValidateTLS(cmd *cli.Command) error

ValidateTLS checks that the TLS configuration is consistent. Call this early (before opening the database) to fail fast on misconfigurations.

type Configurable

type Configurable interface {
	Configure(cfg *AppConfig, cmd *cli.Command) error
}

Configurable is implemented by apps that need to read their configuration and perform setup (create repositories, register icons, wire handlers). Configure receives the shared AppConfig and the parsed CLI command.

type DatabaseConfig

type DatabaseConfig struct {
	DSN string
}

DatabaseConfig holds database settings.

type Enqueuer added in v0.13.0

type Enqueuer interface {
	Enqueue(ctx context.Context, typeName string, payload any) (string, error)
	EnqueueAt(ctx context.Context, typeName string, payload any, runAt time.Time) (string, error)
	Dequeue(ctx context.Context, id string) error
}

Enqueuer provides job submission and cancellation. Use this interface for code that only needs to enqueue jobs, not register handlers.

type FieldError

type FieldError struct {
	Field   string `json:"field"`
	Tag     string `json:"tag"`
	Param   string `json:"param,omitempty"`
	Value   any    `json:"value"`
	Message string `json:"message"`
}

FieldError represents a validation failure on a single field.

type HTTPError

type HTTPError struct {
	Message string
	Code    int
}

HTTPError represents an HTTP error with a status code and message.

func NewHTTPError

func NewHTTPError(code int, message string) *HTTPError

NewHTTPError creates a new HTTPError.

func (*HTTPError) Error

func (e *HTTPError) Error() string

type HandlerFunc

type HandlerFunc func(w http.ResponseWriter, r *http.Request) error

HandlerFunc is an HTTP handler that returns an error. Use Handle() to convert it to a standard http.HandlerFunc.

type HasAdmin

type HasAdmin interface {
	AdminRoutes(r chi.Router)
	AdminNavItems() []NavItem
}

HasAdmin is implemented by apps that contribute admin panel routes and navigation items. AdminRoutes receives a chi router already prefixed with /admin and protected by auth middleware.

type HasCLICommands

type HasCLICommands interface {
	CLICommands() []*cli.Command
}

HasCLICommands is implemented by apps that contribute subcommands.

type HasDependencies

type HasDependencies interface {
	Dependencies() []string
}

HasDependencies is implemented by apps that require other apps to be registered first. Dependencies() returns the names of required apps; registration panics if any are missing.

type HasDocuments added in v0.11.0

type HasDocuments interface {
	Documents() []any
}

HasDocuments is implemented by apps that register Den document types. The returned slice should contain zero-value pointers, e.g. &User{}, &Job{}. Den's Register() creates tables and indexes automatically from the struct tags.

type HasFlags added in v0.10.0

type HasFlags interface {
	Flags(configSource func(key string) cli.ValueSource) []cli.Flag
}

HasFlags is implemented by apps that define CLI flags. The configSource parameter enables TOML file sourcing; it may be nil when only ENV/CLI sources are used.

type HasFuncMap

type HasFuncMap interface {
	FuncMap() template.FuncMap
}

HasFuncMap is implemented by apps that provide static template functions. These are added once at boot time and available in all templates.

type HasJobs

type HasJobs interface {
	RegisterJobs(q Queue)
}

HasJobs is implemented by apps that register background job handlers. Called by the Queue implementation during PostConfigure(), after all apps have been configured and before workers start. This guarantees that state set in Configure() is available when RegisterJobs is invoked.

type HasMiddleware

type HasMiddleware interface {
	Middleware() []func(http.Handler) http.Handler
}

HasMiddleware is implemented by apps that contribute HTTP middleware.

type HasNavItems

type HasNavItems interface {
	NavItems() []NavItem
}

HasNavItems is implemented by apps that contribute navigation items.

type HasRequestFuncMap

type HasRequestFuncMap interface {
	RequestFuncMap(ctx context.Context) template.FuncMap
}

HasRequestFuncMap is implemented by apps that provide context-scoped template functions (e.g., CSRF tokens, current user, translations). These are added per request via middleware using template.Clone(). The context carries all request-scoped values needed by the functions; this enables template rendering outside HTTP handlers (background jobs, SSE broadcasts, CLI commands).

type HasRoutes

type HasRoutes interface {
	Routes(r chi.Router)
}

HasRoutes is implemented by apps that register HTTP routes.

type HasShutdown

type HasShutdown interface {
	Shutdown(ctx context.Context) error
}

HasShutdown is implemented by apps that need to perform cleanup during graceful shutdown (e.g., stopping background goroutines, flushing buffers). Called in reverse registration order before the HTTP server stops.

type HasStaticFiles

type HasStaticFiles interface {
	StaticFS() (prefix string, fsys fs.FS)
}

HasStaticFiles is implemented by apps that contribute static file assets. The returned prefix namespaces the files under the static URL path (e.g., prefix "admin" serves files at /static/admin/...).

type HasTemplates

type HasTemplates interface {
	TemplateFS() fs.FS
}

HasTemplates is implemented by apps that provide HTML template files. The returned fs.FS should contain .html files with {{ define "appname/..." }} blocks. Templates are parsed once at boot time into the global template set.

type HasTranslations

type HasTranslations interface {
	TranslationFS() fs.FS
}

HasTranslations is implemented by apps that contribute translation files. The returned fs.FS must contain a "translations/" directory with TOML files (e.g., "translations/active.en.toml").

type I18nConfig

type I18nConfig struct {
	DefaultLanguage    string
	SupportedLanguages string // comma-separated
}

I18nConfig holds internationalization settings.

type IconFunc added in v0.6.0

type IconFunc = func(...string) template.HTML

IconFunc is the function signature for icon template functions. It matches the signature used by bsicons and other icon packages.

type JobConfig

type JobConfig struct {
	MaxRetries int
	Priority   int
}

JobConfig holds per-handler configuration.

type JobHandlerFunc

type JobHandlerFunc func(ctx context.Context, payload []byte) error

JobHandlerFunc is the signature for job handler functions. The context carries a deadline from the worker's shutdown timeout. Payload is the raw JSON bytes that were passed to Enqueue.

type JobOption

type JobOption func(*JobConfig)

JobOption configures job handler registration.

func WithMaxRetries

func WithMaxRetries(n int) JobOption

WithMaxRetries sets the maximum number of retries for a job type.

func WithPriority added in v0.13.0

func WithPriority(n int) JobOption

WithPriority sets the default priority for a job type. Higher values mean higher urgency — priority 10 jobs are claimed before priority 0 jobs. The default priority is 0.

type NavItem struct {
	Label     string
	LabelKey  string // i18n message ID; translated at render time, falls back to Label
	URL       string
	Icon      template.HTML
	Position  int
	AuthOnly  bool
	AdminOnly bool
}

NavItem represents a navigation entry contributed by an app.

func NavItems(ctx context.Context) []NavItem

NavItems retrieves the navigation items from the context.

type NavLink struct {
	Label    string
	URL      string
	Icon     template.HTML
	IsActive bool
}

NavLink is a template-ready navigation item with pre-computed active state. It is produced by the navLinks template function from the registered NavItems, filtered by the current user's authentication/authorization state.

type PageRequest

type PageRequest struct {
	Limit int // items per page (default 20, max 100)
	Page  int // 1-based page number for offset-based pagination (0 = not used)
}

PageRequest holds pagination parameters parsed from a query string.

func ParsePageRequest

func ParsePageRequest(r *http.Request) PageRequest

ParsePageRequest extracts pagination parameters from the request query string.

func (PageRequest) Offset

func (pr PageRequest) Offset() int

Offset returns the SQL OFFSET for the current page. Page 0 and 1 both return offset 0.

type PageResponse

type PageResponse[T any] struct {
	Items      []T        `json:"items"`
	Pagination PageResult `json:"pagination"`
}

PageResponse wraps items with pagination metadata for JSON APIs. Use with OffsetResult to populate the Pagination field:

return burrow.PageResponse[Item]{
    Items:      items,
    Pagination: burrow.OffsetResult(pr, totalCount),
}

type PageResult

type PageResult struct {
	HasMore    bool `json:"has_more"`              // convenience: more pages exist
	Page       int  `json:"page,omitempty"`        // current page number (1-based)
	TotalPages int  `json:"total_pages,omitempty"` // total number of pages
	TotalCount int  `json:"total_count,omitempty"` // total number of items
}

PageResult holds pagination metadata returned alongside items.

func OffsetResult

func OffsetResult(pr PageRequest, totalCount int) PageResult

OffsetResult builds a PageResult for offset-based pagination.

func (PageResult) PageSize added in v0.6.0

func (pr PageResult) PageSize() int

PageSize returns the number of items per page, derived from TotalCount and TotalPages. Returns 0 when TotalPages is zero.

type PostConfigurable added in v0.7.4

type PostConfigurable interface {
	PostConfigure(cfg *AppConfig, cmd *cli.Command) error
}

PostConfigurable is implemented by apps that need a second configuration pass after all Configurable apps have been configured. This is useful when an app needs to interact with other apps' state that is only available after Configure() has run (e.g., the jobs app discovering HasJobs handlers). PostConfigure is called once, after all Configure() calls have completed.

type Queue

type Queue interface {
	Enqueuer
	Handle(typeName string, fn JobHandlerFunc, opts ...JobOption)
}

Queue provides job handler registration, enqueueing, and cancellation. contrib/jobs provides a SQLite-backed implementation.

type ReadinessChecker added in v0.4.0

type ReadinessChecker interface {
	ReadinessCheck(ctx context.Context) error
}

ReadinessChecker is implemented by apps that contribute to the readiness probe. ReadinessCheck returns nil when the app is ready to serve traffic, or an error describing what is not ready.

type Registry

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

Registry holds registered apps in insertion order and provides methods to collect capabilities (routes, middleware, flags, etc.) from all apps. Application code typically does not interact with Registry directly — Server manages it internally. Contrib app authors may use Registry.Get to look up sibling apps during Configure.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty Registry.

func (*Registry) Add

func (r *Registry) Add(app App)

Add registers an app. It panics if an app with the same name has already been registered or if a declared dependency is missing (programming errors caught at startup).

func (*Registry) AllAdminNavItems

func (r *Registry) AllAdminNavItems() []NavItem

AllAdminNavItems collects AdminNavItems from all HasAdmin apps and returns them sorted by Position (stable sort preserves insertion order for equal positions).

func (*Registry) AllCLICommands

func (r *Registry) AllCLICommands() []*cli.Command

AllCLICommands collects CLI subcommands from all HasCLICommands apps.

func (*Registry) AllFlags

func (r *Registry) AllFlags(configSource func(key string) cli.ValueSource) []cli.Flag

AllFlags collects CLI flags from all HasFlags apps. Pass configSource to enable TOML file sourcing (or nil for ENV-only).

func (*Registry) AllNavItems

func (r *Registry) AllNavItems() []NavItem

AllNavItems collects NavItems from all HasNavItems apps and returns them sorted by Position (stable sort preserves insertion order for equal positions).

func (*Registry) Apps

func (r *Registry) Apps() []App

Apps returns all registered apps in the order they were added.

func (*Registry) Configure

func (r *Registry) Configure(cfg *AppConfig, cmd *cli.Command) error

Configure calls Configure on each Configurable app, then calls PostConfigure on each PostConfigurable app. This two-phase approach guarantees that all apps are fully configured before any PostConfigure runs, which is important for apps that need to interact with other apps' state (e.g., the jobs app discovering HasJobs handlers).

func (*Registry) ConfigureAll added in v0.10.0

func (r *Registry) ConfigureAll(cfg *AppConfig) error

ConfigureAll calls Configure on each Configurable app, passing the given AppConfig with a nil *cli.Command. This is a test convenience; the real boot sequence uses Registry.Configure.

func (*Registry) Get

func (r *Registry) Get(name string) (App, bool)

Get returns the app with the given name, or false if not found.

func (*Registry) RegisterDocuments added in v0.11.0

func (r *Registry) RegisterDocuments(ctx context.Context, db *den.DB) error

RegisterDocuments registers document types from all HasDocuments apps with the Den database. This creates tables and indexes automatically.

func (*Registry) RegisterMiddleware

func (r *Registry) RegisterMiddleware(router chi.Router)

RegisterMiddleware applies middleware from all HasMiddleware apps to the chi router, in app registration order.

func (*Registry) RegisterRoutes

func (r *Registry) RegisterRoutes(router chi.Router)

RegisterRoutes calls Routes on each HasRoutes app, allowing apps to register their HTTP handlers.

func (*Registry) Seed

func (r *Registry) Seed(ctx context.Context) error

Seed calls Seed on each Seedable app in order. It stops and returns on the first error.

func (*Registry) Shutdown

func (r *Registry) Shutdown(ctx context.Context) error

Shutdown calls Shutdown on each HasShutdown app in reverse registration order. Errors are collected but do not prevent other apps from shutting down.

type ResultCapture added in v0.13.0

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

ResultCapture collects a handler's return value so the worker can persist it. The worker injects a ResultCapture into the handler context; result-aware handlers (via ResultTask) write their marshalled result into it.

func (*ResultCapture) Result added in v0.13.0

func (rc *ResultCapture) Result() string

Result returns the captured JSON result, or empty string if nothing was captured.

type ResultTask added in v0.13.0

type ResultTask[P, R any] struct {
	// contains filtered or unexported fields
}

ResultTask is a type-safe wrapper for a job type whose handler returns both a result value and an error. It handles JSON marshalling of both payload and result automatically. The result is communicated back to the worker via the context's ResultCapture.

Create with DefineResultTask, wire with Register, enqueue with Enqueue:

var compute = burrow.DefineResultTask[Input, Output]("compute",
    func(ctx context.Context, in Input) (Output, error) { ... },
)

func (a *App) RegisterJobs(q burrow.Queue) { compute.Register(q) }

compute.Enqueue(ctx, Input{...})

func DefineResultTask added in v0.13.0

func DefineResultTask[P, R any](name string, handler func(context.Context, P) (R, error), opts ...JobOption) *ResultTask[P, R]

DefineResultTask creates a typed task definition with a result-returning handler. Call Register in your RegisterJobs method to wire it to a Queue.

func (*ResultTask[P, R]) Enqueue added in v0.13.0

func (t *ResultTask[P, R]) Enqueue(ctx context.Context, payload P) (string, error)

Enqueue marshals the payload and enqueues the task for immediate processing. Panics if called before Register.

func (*ResultTask[P, R]) EnqueueAt added in v0.13.0

func (t *ResultTask[P, R]) EnqueueAt(ctx context.Context, payload P, runAt time.Time) (string, error)

EnqueueAt marshals the payload and enqueues the task for processing at the given time. Panics if called before Register.

func (*ResultTask[P, R]) Name added in v0.13.0

func (t *ResultTask[P, R]) Name() string

Name returns the task type name.

func (*ResultTask[P, R]) Register added in v0.13.0

func (t *ResultTask[P, R]) Register(q Queue)

Register wires the task to a Queue. It registers a JobHandlerFunc that unmarshals the payload, calls the typed handler, marshals the result into the context's ResultCapture, and returns the error.

type Seedable

type Seedable interface {
	Seed(ctx context.Context) error
}

Seedable is implemented by apps that can seed the database with initial data. Seed functions only run when the server is started with the --seed flag (or SEED=true environment variable). Implementations should be idempotent — safe to call multiple times without creating duplicates.

type Server

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

Server is the main framework entry point that orchestrates the full application lifecycle. Typical usage:

  1. Create with NewServer (registers apps, sorts by dependencies)
  2. Configure the layout with Server.SetLayout
  3. Collect CLI flags with Server.Flags
  4. Start with Server.Run (opens DB, migrates, bootstraps, serves HTTP)

func NewServer

func NewServer(apps ...App) *Server

NewServer creates a Server and registers the given apps. Apps are automatically sorted so that dependencies are registered before the apps that need them. The relative order of independent apps is preserved. NewServer panics if a dependency is missing from the input or if there is a dependency cycle.

func (*Server) Flags

func (s *Server) Flags(configSource func(key string) cli.ValueSource) []cli.Flag

Flags returns all CLI flags: core framework flags merged with flags from all Configurable apps. Pass a configSource to enable TOML file sourcing (or nil for ENV-only).

func (*Server) Registry

func (s *Server) Registry() *Registry

Registry returns the server's app registry.

func (*Server) Run

func (s *Server) Run(ctx context.Context, cmd *cli.Command) error

Run is a cli.ActionFunc that boots and starts the server. It opens the database, runs migrations, bootstraps apps, configures apps, and starts the HTTP server with graceful shutdown.

func (*Server) SetLayout

func (s *Server) SetLayout(name string)

SetLayout configures the layout template name used for all pages.

func (*Server) TemplateExecutor added in v0.9.0

func (s *Server) TemplateExecutor() TemplateExecutor

TemplateExecutor returns the server's template executor function. Use this after boot to obtain an executor for non-HTTP rendering via WithTemplateExecutor and RenderFragment. Returns nil if templates have not been built yet (i.e., before Server.Run).

type ServerConfig

type ServerConfig struct {
	Host            string
	BaseURL         string
	PIDFile         string
	Port            int
	MaxBodySize     int // in MB
	ShutdownTimeout int // in seconds
	Seed            bool
}

ServerConfig holds HTTP server settings.

type Startable added in v0.9.0

type Startable interface {
	Start(srv *Server) error
}

Startable is implemented by apps that need to start background processes after the full boot sequence completes (templates built, middleware and routes registered). It is the counterpart to HasShutdown.

type TLSConfig

type TLSConfig struct {
	Mode     string // auto, acme, selfsigned, manual, off
	CertDir  string
	Email    string
	CertFile string
	KeyFile  string
}

TLSConfig holds TLS settings.

type TaskDefinition added in v0.13.0

type TaskDefinition[P any] struct {
	// contains filtered or unexported fields
}

TaskDefinition is a type-safe wrapper for a job type. It handles JSON marshalling/unmarshalling of the payload automatically, ensuring that the enqueue site and handler always agree on the payload type at compile time.

Create a TaskDefinition with DefineTask, wire it to a Queue with Register (typically in your RegisterJobs method), then use Enqueue to submit work:

var sendEmail = burrow.DefineTask[EmailPayload]("send-email",
    func(ctx context.Context, p EmailPayload) error { ... },
    burrow.WithMaxRetries(5),
)

func (a *App) RegisterJobs(q burrow.Queue) { sendEmail.Register(q) }

sendEmail.Enqueue(ctx, EmailPayload{To: "x@y.com"})

func DefineTask added in v0.13.0

func DefineTask[P any](name string, handler func(context.Context, P) error, opts ...JobOption) *TaskDefinition[P]

DefineTask creates a typed task definition. Call Register in your RegisterJobs method to wire it to a Queue before enqueueing work.

func (*TaskDefinition[P]) Enqueue added in v0.13.0

func (t *TaskDefinition[P]) Enqueue(ctx context.Context, payload P) (string, error)

Enqueue marshals the payload and enqueues the task for immediate processing. Panics if called before Register.

func (*TaskDefinition[P]) EnqueueAt added in v0.13.0

func (t *TaskDefinition[P]) EnqueueAt(ctx context.Context, payload P, runAt time.Time) (string, error)

EnqueueAt marshals the payload and enqueues the task for processing at the given time. Panics if called before Register.

func (*TaskDefinition[P]) Name added in v0.13.0

func (t *TaskDefinition[P]) Name() string

Name returns the task type name.

func (*TaskDefinition[P]) Register added in v0.13.0

func (t *TaskDefinition[P]) Register(q Queue)

Register wires the task to a Queue. It registers a JobHandlerFunc that automatically unmarshals the JSON payload into P before calling the typed handler, and stores the queue reference for Enqueue/EnqueueAt.

type TemplateExecutor

type TemplateExecutor func(ctx context.Context, name string, data map[string]any) (template.HTML, error)

TemplateExecutor executes a named template with the given data and returns the rendered HTML. It is stored in the request context by the template middleware and used by Render. The context carries all request-scoped values needed for template rendering.

func TemplateExec added in v0.5.0

func TemplateExec(ctx context.Context) TemplateExecutor

TemplateExec retrieves the template executor from the context.

func TemplateExecutorFromContext

func TemplateExecutorFromContext(ctx context.Context) TemplateExecutor

TemplateExecutorFromContext is a deprecated alias for TemplateExec.

type ValidationError

type ValidationError struct {
	Errors []FieldError
}

ValidationError is returned by Bind()/Validate() when validation fails.

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) HasField

func (e *ValidationError) HasField(name string) bool

HasField reports whether the validation error contains a failure for the named field.

func (*ValidationError) Translate

func (e *ValidationError) Translate(ctx context.Context, translateData func(context.Context, string, map[string]any) string)

Translate translates field error messages using the given translation function. The translateData function receives a key and template data, and returns the translated string. Typically called with i18n.TData:

ve.Translate(ctx, i18n.TData)

Directories

Path Synopsis
contrib
admin
Package admin provides the admin panel coordinator as a burrow contrib app.
Package admin provides the admin panel coordinator as a burrow contrib app.
alpine
Package alpine provides Alpine.js as a burrow contrib app.
Package alpine provides Alpine.js as a burrow contrib app.
auth
Package auth provides authentication as a burrow contrib app.
Package auth provides authentication as a burrow contrib app.
auth/authtest
Package authtest provides test helpers for creating auth-migrated databases and test users, following the convention of net/http/httptest.
Package authtest provides test helpers for creating auth-migrated databases and test users, following the convention of net/http/httptest.
authmail
Package authmail defines the Renderer interface for auth email templates.
Package authmail defines the Renderer interface for auth email templates.
authmail/smtpmail
Package smtpmail provides an SMTP-based implementation of auth.EmailService with pluggable email templates via authmail.Renderer.
Package smtpmail provides an SMTP-based implementation of auth.EmailService with pluggable email templates via authmail.Renderer.
bootstrap
Package bootstrap provides a design system contrib app using Bootstrap 5, Bootstrap Icons, and htmx.
Package bootstrap provides a design system contrib app using Bootstrap 5, Bootstrap Icons, and htmx.
bsicons
Package bsicons provides all Bootstrap Icons as inline SVG template.HTML values.
Package bsicons provides all Bootstrap Icons as inline SVG template.HTML values.
bsicons/internal/generate command
Command generate reads Bootstrap Icons SVG files and outputs a Go source file containing the icon data and named accessor functions.
Command generate reads Bootstrap Icons SVG files and outputs a Go source file containing the icon data and named accessor functions.
csrf
Package csrf provides CSRF protection as a burrow contrib app.
Package csrf provides CSRF protection as a burrow contrib app.
healthcheck
Package healthcheck provides liveness and readiness probes for burrow.
Package healthcheck provides liveness and readiness probes for burrow.
htmx
Package htmx provides request detection and response helpers for htmx, inspired by django-htmx.
Package htmx provides request detection and response helpers for htmx, inspired by django-htmx.
humanize
Package humanize provides i18n-aware template functions for human-friendly display of times, numbers, and file sizes.
Package humanize provides i18n-aware template functions for human-friendly display of times, numbers, and file sizes.
messages
Package messages provides flash message support as a burrow contrib app.
Package messages provides flash message support as a burrow contrib app.
ratelimit
Package ratelimit provides per-client rate limiting as a burrow contrib app.
Package ratelimit provides per-client rate limiting as a burrow contrib app.
session
Package session provides cookie-based session management as a burrow contrib app.
Package session provides cookie-based session management as a burrow contrib app.
sse
Package sse provides a Server-Sent Events (SSE) contrib app for burrow.
Package sse provides a Server-Sent Events (SSE) contrib app for burrow.
staticfiles
Package staticfiles provides static file serving as a burrow contrib app.
Package staticfiles provides static file serving as a burrow contrib app.
uploads
Package uploads provides file upload storage as a burrow contrib app.
Package uploads provides file upload storage as a burrow contrib app.
example
hello command
Command hello is a minimal burrow application that serves a single "Hello, World!" page with Bootstrap styling and i18n support.
Command hello is a minimal burrow application that serves a single "Hello, World!" page with Bootstrap styling and i18n support.
notes/cmd/server command
Command server demonstrates how to build an application using the burrow framework with contrib apps.
Command server demonstrates how to build an application using the burrow framework with contrib apps.
notes/internal/notes
Package notes is an example custom app demonstrating the burrow framework.
Package notes is an example custom app demonstrating the burrow framework.
notes/internal/pages
Package pages provides the example app's static pages (homepage), layout rendering, and icon template functions.
Package pages provides the example app's static pages (homepage), layout rendering, and icon template functions.
Package forms provides Django-style form structs for creation, binding, validation, and field metadata extraction.
Package forms provides Django-style form structs for creation, binding, validation, and field metadata extraction.
Package i18n provides internationalization as core framework infrastructure.
Package i18n provides internationalization as core framework infrastructure.
internal
cryptokey
Package cryptokey provides shared utilities for resolving and decoding hex-encoded 32-byte cryptographic keys used by contrib apps (csrf, session).
Package cryptokey provides shared utilities for resolving and decoding hex-encoded 32-byte cryptographic keys used by contrib apps (csrf, session).

Jump to

Keyboard shortcuts

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