Core Interfaces¶
All interfaces are defined in the burrow package (github.com/oliverandrich/burrow).
Required¶
App¶
Every app must implement this interface:
Name()returns a unique identifier for the app (e.g.,"auth","notes")Register()receives the sharedAppConfigfor initialisation
type myApp struct {
repo *Repository
}
func (a *myApp) Name() string { return "notes" }
func (a *myApp) Register(cfg *burrow.AppConfig) error {
a.repo = NewRepository(cfg.DB)
return nil
}
AppConfig¶
Passed to every app's Register method:
| Field | Description |
|---|---|
DB |
Bun database connection (SQLite with WAL mode) |
Registry |
App registry for looking up other apps |
Config |
Parsed framework configuration |
Optional¶
Apps can implement any combination of these interfaces. The framework detects them via type assertion and calls the appropriate methods during the boot sequence.
Migratable¶
Provides an fs.FS containing .up.sql migration files at the root level. Called during startup before Register(). When using //go:embed migrations, you must strip the directory prefix:
//go:embed migrations
var migrationFS embed.FS
func (a *App) MigrationFS() fs.FS {
sub, _ := fs.Sub(migrationFS, "migrations")
return sub
}
See the Migrations guide for details on file naming and tracking.
HasRoutes¶
Registers HTTP handlers on the Chi router. Called after all apps are registered.
func (a *App) Routes(r chi.Router) {
r.Route("/notes", func(r chi.Router) {
r.Get("/", burrow.Handle(a.handleList))
r.Get("/{id}", burrow.Handle(a.handleDetail))
r.Group(func(r chi.Router) {
r.Use(auth.RequireAuth())
r.Post("/", burrow.Handle(a.handleCreate))
})
})
}
See the Routing guide for details on handlers, URL parameters, and middleware.
HasMiddleware¶
Returns middleware functions applied globally to the router. Applied in app registration order.
func (a *App) Middleware() []func(http.Handler) http.Handler {
return []func(http.Handler) http.Handler{
a.sessionMiddleware,
}
}
HasNavItems¶
Returns navigation entries collected into the request context by the framework:
type NavItem struct {
Label string
LabelKey string // i18n message ID
URL string
Icon template.HTML // inline SVG, empty string for no icon
Position int
AuthOnly bool
AdminOnly bool
}
func (a *App) NavItems() []burrow.NavItem {
return []burrow.NavItem{
{
Label: "Notes",
URL: "/notes",
Position: 20,
AuthOnly: true,
},
}
}
See the Navigation guide for positioning and ordering.
HasTemplates¶
Returns an fs.FS containing .html template files. Templates must use {{ define "appname/templatename" }} blocks to namespace themselves. All template files from all apps are parsed into a single global *template.Template at boot time.
//go:embed templates/*.html
var templateFS embed.FS
func (a *App) TemplateFS() fs.FS {
sub, _ := fs.Sub(templateFS, "templates")
return sub
}
See the Layouts & Rendering guide for details on template rendering and layout wrapping.
HasFuncMap¶
Returns a static template.FuncMap added at parse time. Functions are available globally in all templates. The framework panics if two apps register the same function name.
func (a *App) FuncMap() template.FuncMap {
return template.FuncMap{
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02")
},
}
}
Reserved function names
The following names are already registered by the framework and contrib apps:
safeHTML, safeURL, safeAttr, itoa, lang, staticURL, csrfToken, t, tData, tPlural, currentUser, isAuthenticated, add, sub, pageURL, pageLimit, pageNumbers.
Do not use these names in your own FuncMap — the server will panic at startup.
HasRequestFuncMap¶
Returns request-scoped template functions that are injected per-request via template.Clone(). Use this for functions that depend on the request context (e.g., current user, CSRF token, locale).
func (a *App) RequestFuncMap(r *http.Request) template.FuncMap {
return template.FuncMap{
"currentUser": func() *User {
return UserFromContext(r.Context())
},
"isAuthenticated": func() bool {
return UserFromContext(r.Context()) != nil
},
}
}
Configurable¶
type Configurable interface {
Flags(configSource func(key string) cli.ValueSource) []cli.Flag
Configure(cmd *cli.Command) error
}
Flags()returns CLI flags merged into the application's flag set. TheconfigSourceparameter enables TOML file sourcing — passnilwhen no config file is used.Configure()is called after CLI parsing to read flag values
func (a *App) Flags(configSource func(key string) cli.ValueSource) []cli.Flag {
return []cli.Flag{
&cli.IntFlag{
Name: "notes-page-size",
Value: 20,
Usage: "Number of notes per page",
Sources: burrow.FlagSources(configSource, "NOTES_PAGE_SIZE", "notes.page_size"),
},
}
}
func (a *App) Configure(cmd *cli.Command) error {
a.pageSize = int(cmd.Int("notes-page-size"))
return nil
}
See the Configuration guide for the three-tier config system.
HasCLICommands¶
Returns CLI subcommands (e.g., promote, demote). Collect them with srv.Registry().AllCLICommands().
func (a *App) CLICommands() []*cli.Command {
return []*cli.Command{
{
Name: "seed-notes",
Usage: "Create sample notes for testing",
Action: func(ctx context.Context, cmd *cli.Command) error {
return a.seedNotes(ctx)
},
},
}
}
Seedable¶
Seeds the database with initial data. Called automatically during startup after migrations and app registration. Seeders run in app registration order and stop on the first error.
func (a *App) Seed(ctx context.Context) error {
count, _ := a.repo.CountCategories(ctx)
if count > 0 {
return nil // already seeded
}
return a.repo.CreateCategories(ctx, defaultCategories)
}
HasStaticFiles¶
Contributes static file assets that the staticfiles app collects and serves. The prefix namespaces files under the static URL path (e.g., prefix "auth" serves files at /static/auth/...). Files are content-hashed and cache-busted just like user-provided static files.
//go:embed static
var staticFS embed.FS
func (a *App) StaticFS() (string, fs.FS) {
sub, _ := fs.Sub(staticFS, "static")
return "myapp", sub
}
HasAdmin¶
Contributes admin panel routes and navigation items. AdminRoutes receives a Chi router already prefixed with /admin and protected by auth middleware. The admin contrib app discovers all HasAdmin implementations and mounts them.
func (a *App) AdminRoutes(r chi.Router) {
r.Get("/notes", burrow.Handle(a.adminListNotes))
r.Get("/notes/{id}", burrow.Handle(a.adminNoteDetail))
}
func (a *App) AdminNavItems() []burrow.NavItem {
return []burrow.NavItem{
{Label: "Notes", URL: "/admin/notes", Position: 30},
}
}
See the Admin contrib app for the full admin panel setup.
HasTranslations¶
Contributes translation files for the i18n app. The returned fs.FS must contain TOML files (e.g., active.en.toml, active.de.toml). The i18n app auto-discovers all HasTranslations implementations at startup.
//go:embed translations
var translationFS embed.FS
func (a *App) TranslationFS() fs.FS { return translationFS }
See the i18n contrib app for translation file format and usage.
HasDependencies¶
Returns app names that must be registered before this app. NewServer automatically sorts apps by dependencies. The registry panics at startup if any dependency is missing.
HasShutdown¶
Performs cleanup during graceful shutdown (e.g., stopping background goroutines, flushing buffers). Called in reverse registration order before the HTTP server stops. Errors are logged but do not prevent other apps from shutting down. The context carries the server's shutdown timeout.