xtemplate

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: Apache-2.0 Imports: 50 Imported by: 6

README

xtemplate

Hypermedia web apps from a directory of Go templates.

xtemplate is a Go server that treats templates as handlers: file-based routing, a request-scoped dot context, and first-class static files; no separate application layer.

<!-- contacts.html  →  GET /contacts (SQL provider configured as .DB) -->
<ul>
  {{range .DB.QueryRows `SELECT id, name FROM contacts`}}
  <li><a href="/contacts/{{.id}}">{{.name}}</a></li>
  {{end}}
</ul>
<!-- contacts/{id}.html  →  GET /contacts/{id} -->
{{$id := .Req.PathValue `id`}}
{{$c := .DB.QueryRow `SELECT id, name, phone, email FROM contacts WHERE id = ?` $id}}
<form method="POST">
  <input name="name"  value="{{$c.name}}">
  <input name="phone" value="{{$c.phone}}">
  <input name="email" value="{{$c.email}}">
  <button>Update</button>
</form>

{{define "POST /contacts/{id}"}}
{{$_ := .DB.Exec `UPDATE contacts SET name=?, phone=?, email=? WHERE id = ?`
    (.Req.FormValue `name`) (.Req.FormValue `phone`)
    (.Req.FormValue `email`) (.Req.PathValue `id`)}}
{{template `/contacts/{id}.html` .}}
{{end}}

No route table, no handler functions. One file for the index. One file for the form and the update. Path parameters, form values, and SQL all hang off the dot context.

Philosophy

Deal with the first-class citizens of the web: paths, requests, HTML responses, and backing data. Templates are expressive enough to be the app. More details in Design.

Highlights

  • File-based routing - admin/settings.html serves GET /admin/settings; index.html serves the directory. Template semantics
  • Any method and pattern - {{define "DELETE /contact/{id}"}} is a route. Instance loading
  • Loads once, reloads live - parse at startup; swap an immutable instance on change. Design
  • Dot context - .Req, .Resp, .DB, .FS, … per request. Dot context
  • Safe by default - html/template escaping; optional sanitize / trust helpers. Functions
  • Optimal static files - hashes, SRI, precompressed encodings, long cache. Instance loading
  • SSE - {{define "SSE /path"}} for live updates. Dot context → Flush
  • Embeddable - CLI, Docker, Caddy plugin, or http.Handler library. Deployment modes

Some more patterns:

{{- with $hash := .X.StaticFileHash `/assets/reset.css`}}
<link rel="stylesheet" href="/assets/reset.css?hash={{$hash}}" integrity="{{$hash}}">
{{- end}}
{{- define "SSE /reload"}}{{.Flush.WaitForServerStop}}data: reload{{printf "\n\n"}}{{end}}
<script>new EventSource("/reload").onmessage = () => location.reload()</script>

More complete apps: examples/.

Quick start

If you want… Start here
Step-by-step first app Getting started tutorial
Zero setup container Docker
Local templates + live reload CLI watchfs · CLI flags
Templates from a Git remote CLI git
Automatic HTTPS, auth, proxy Caddy module
Embed in your Go program Go library
# CLI (live reload)
go install github.com/infogulch/xtemplate/cmd/watchfs@latest
mkdir -p templates && echo '<h1>{{.Req.URL.Path}}</h1>' > templates/index.html
watchfs --listen :8080
# open http://localhost:8080
# Docker
docker run --rm -p 8080:80 \
  -v "$PWD/templates:/app/templates:ro" \
  infogulch/xtemplate:latest
# Caddy
:8080
route {
	xtemplate
}

Full integration map and configs: docs/reference/deployment-modes.md.

All documentation (tutorial, how-tos, reference, design): docs/.

Users

Contributing

Development setup, repo map, and tests: CONTRIBUTING.md (same as docs/contributing.md).

History and license

Evolved from go-htmx and a Caddy-centric prototype into a standalone library with an optional Caddy module. Narrative: Project history. Releases: CHANGELOG.md.

Licensed under the Apache 2.0 license. See LICENSE.

Documentation

Overview

xtemplate extends Go's html/template to be capable enough to define an entire server-side application with a directory of Go templates.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddBlueMondayPolicy added in v0.5.0

func AddBlueMondayPolicy(name string, policy *bluemonday.Policy)

AddBlueMondayPolicy adds a bluemonday policy to the global policy list available to all xtemplate instances.

func AddMarkdownConfig added in v0.9.0

func AddMarkdownConfig(name string, md goldmark.Markdown)

AddMarkdownConfig adds a custom markdown configuration to xtemplate's markdown config map, shared by all xtemplate instances.

func FuncFailf added in v0.7.0

func FuncFailf(format string, args ...any) (string, error)

func FuncHumanize added in v0.5.0

func FuncHumanize(formatType, data string) (string, error)

humanize transforms size and time inputs to a human readable format using the go-humanize library.

Call with two parameters: format type and value to format. Supported format types are:

"size" which turns an integer amount of bytes into a string like "2.3 MB", for example:

{{humanize "size" "2048000"}}

"time" which turns a time string into a relative time string like "2 weeks ago", for example:

{{humanize "time" "Fri, 05 May 2022 15:04:05 +0200"}}

func FuncIdx added in v0.5.0

func FuncIdx(idx int, arr any) (any, error)

idx gets an item from a list, similar to the built-in index, but with reversed args: index first, then array. This is useful to use index in a pipeline, for example:

{{generate-list | idx 5}}

Returns an error if the value is not indexable or the index is out of range.

func FuncReturn added in v0.5.0

func FuncReturn() (string, error)

return causes the template to exit early with a success status.

func FuncSanitizeHtml added in v0.5.0

func FuncSanitizeHtml(policyName string, html string) (template.HTML, error)

sanitizeHtml Uses the BlueMonday library to sanitize strings with html content. First parameter is the name of the chosen sanitization policy.

func FuncTrustAttr added in v0.5.0

func FuncTrustAttr(s string) template.HTMLAttr

trustAttr marks the string s as safe and does not escape its contents in html attribute context.

func FuncTrustHtml added in v0.5.0

func FuncTrustHtml(s string) template.HTML

trustHtml marks the string s as safe and does not escape its contents in html node context.

func FuncTrustJS added in v0.5.0

func FuncTrustJS(s string) template.JS

trustJS marks the string s as safe and does not escape its contents in script tag context.

func FuncTrustJSStr added in v0.5.0

func FuncTrustJSStr(s string) template.JSStr

trustJSStr marks the string s as safe and does not escape its contents in script expression context.

func FuncTrustSrcSet added in v0.5.0

func FuncTrustSrcSet(s string) template.Srcset

trustSrcSet marks the string s as safe and does not escape its contents in script tag context.

func FuncTry added in v0.5.0

func FuncTry(fn any, args ...any) (*result, error)

The try template func accepts a fallible function object and calls it with the provided args. If the function and args are valid, try returns the result wrapped in a result object that exposes the return value and error to templates. Useful if you want to call a function and handle its error in a template. If the function value is invalid or the args cannot be used to call it then try raises an error that stops template execution.

func GetLogger added in v0.6.0

func GetLogger(ctx context.Context) *slog.Logger

func GetRequestId added in v0.6.0

func GetRequestId(ctx context.Context) string

func Register added in v0.10.0

func Register(name string, ctor func() DotConfig)

Register makes a provider type available to resolveProviders. Call from init(). Panics on duplicate registration (names the type in the message; the registering package is identified by the runtime's stack trace).

Types

type CleanupDotProvider added in v0.5.0

type CleanupDotProvider interface {
	DotConfig
	Cleanup(any, error) error
}

type Config added in v0.3.0

type Config struct {
	// The path to the templates directory within the filesystem. Default `templates`.
	TemplatesDir string `json:"templates_dir,omitempty" arg:"-t,--template-dir,--templates-dir" default:"templates"`

	// The FS to load templates from. Default: a FS made from the current working directory.
	TemplatesFS afero.Fs `json:"-" arg:"-"`

	// File extension to search for to find template files. Default `.html`.
	TemplateExtension string `json:"template_extension,omitempty" arg:"--template-ext" default:".html"`

	// Whether html templates are minified at load time. Default `true`.
	//
	// This is a *bool to distinguish unset (nil) from set false.
	Minify *bool `json:"minify,omitempty" arg:"-m,--minify" default:"true"`

	CrossOrigin CrossOriginConfig `json:"crossorigin" arg:"-"`

	ProvidersRaw []json.RawMessage `json:"providers,omitempty" arg:"-"`
	Providers    []DotConfig       `json:"-" arg:"-"`

	// Encodings to pre-compress static files into at load time. Supported values:
	// "gzip", "zstd", "br". Default empty (no pre-compression).
	Precompress []string `json:"precompress,omitempty" arg:"--precompress,separate"`

	// Left template action delimiter. Default `{{`.
	LDelim string `json:"left,omitempty" arg:"--ldelim" default:"{{"`

	// Right template action delimiter. Default `}}`.
	RDelim string `json:"right,omitempty" arg:"--rdelim" default:"}}"`

	// Additional functions to add to the template execution context.
	FuncMaps []template.FuncMap `json:"-" arg:"-"`

	// Peer HTTP handlers registered on the instance ServeMux next to template
	// and static routes. Use this to embed existing handlers (APIs, webhooks,
	// health checks) under the same ServeMux as the template app. Errors if the
	// pattern conflicts with another route registered by the template root.
	//
	// Each entry is a sibling route on the mux (net/http.ServeMux pattern).
	// Re-registered when a new instance is built, so they survive reloads.
	Handlers []HandlerRoute `json:"-" arg:"-"`

	// The instance context that is threaded through dot providers and can
	// cancel the server. Defaults to `context.Background()`.
	Ctx context.Context `json:"-" arg:"-"`

	// The default logger. Defaults to `slog.Default()`.
	Logger *slog.Logger `json:"-" arg:"-"`

	// Reload, when non-nil, triggers server.Reload(opts...) on each receive.
	// Send options to mutate the config for that reload (e.g. WithTemplateFS to
	// swap the templates source). Send nil to reload in place. Close the channel
	// to stop the consumer. The caller owns the source and any debounce.
	//
	// Options apply to a copy of the config per reload, so they are not sticky:
	// the next reload starts from the original config again. Use a single reload
	// source per server unless you persist options into the base config yourself.
	Reload <-chan []Option `json:"-" arg:"-"`
}

func New added in v0.1.4

func New() (c *Config)

func (*Config) Instance added in v0.4.0

func (config *Config) Instance(cfgs ...Option) (*Instance, *InstanceStats, []InstanceRoute, error)

Instance creates a new *Instance from the given config

func (*Config) Options added in v0.6.5

func (c *Config) Options(options ...Option) (*Config, error)

func (Config) Server added in v0.4.0

func (config Config) Server(cfgs ...Option) (*Server, error)

Build creates a new Server from an xtemplate.Config.

func (*Config) SetDefaults added in v0.9.0

func (config *Config) SetDefaults() *Config

FillDefaults sets default values for unset fields

type CrossOriginConfig added in v0.9.0

type CrossOriginConfig struct {
	Disabled               bool     `json:"disabled" arg:"--disable-cors" default:"false"`
	TrustedOrigins         []string `json:"trusted_origins" arg:"--trusted-origin,separate"`
	InsecureBypassPatterns []string `json:"insecure_bypass_patterns" arg:"--insecure-bypass-pattern,separate"`
}

type DotConfig added in v0.5.0

type DotConfig interface {
	FieldName() string
	Init(context.Context) error
	// Value returns the value to assign to this provider's dot field for a
	// given request. It must return a stable, non-nil concrete type: makeDot
	// calls it once with a mock request purely to infer the field type via
	// reflection, so a nil return (e.g. on error during inference) cannot be
	// used to build the dot struct.
	Value(Request) (any, error)
}

type DotFlush added in v0.5.0

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

DotFlush is used as the .Flush field for flushing template handlers (SSE).

func (*DotFlush) Flush added in v0.5.0

func (f *DotFlush) Flush() string

Flush flushes any content waiting to be written to the client.

func (*DotFlush) Repeat added in v0.5.0

func (f *DotFlush) Repeat(max_ ...int) <-chan int

Repeat generates numbers up to max, using math.MaxInt64 if no max is provided.

func (*DotFlush) SendSSE added in v0.6.0

func (f *DotFlush) SendSSE(args ...string) error

SendSSE sends an sse message by formatting the provided args as an sse event:

Requires 1-4 args: event, data, id, retry

func (*DotFlush) Sleep added in v0.5.0

func (f *DotFlush) Sleep(ms int) (string, error)

Sleep sleeps for ms milliseconds. Template execution is aborted if the request is canceled or the server receives a stop signal.

func (*DotFlush) WaitForServerStop added in v0.5.0

func (f *DotFlush) WaitForServerStop() (string, error)

WaitForServerStop blocks template execution until the server receives a stop signal, then continues to allow sending a final response before the request is closed. Template execution is aborted if the client cancels the request.

type DotReq added in v0.5.0

type DotReq struct {
	*http.Request
}

DotReq is used as the .Req field for template invocations with an associated request, and contains the current HTTP request struct which can be used to read request data. See http.Request for detailed documentation. Some notable methods and fields:

http.Request.Method, http.Request.PathValue, http.Request.URL, [http.Request.URL.Query], http.Request.Cookie, http.Request.Header, [http.Request.Header.Get].

Note that http.Request.ParseForm must be called before using http.Request.Form, http.Request.PostForm, and http.Request.PostValue.

type DotResp added in v0.5.0

type DotResp struct {
	http.Header
	// contains filtered or unexported fields
}

DotResp is used as the .Resp field in buffered template invocations.

func (*DotResp) AddHeader added in v0.5.0

func (h *DotResp) AddHeader(field, val string) string

AddHeader adds a header field value, appending val to existing values for that field. It returns an empty string.

func (*DotResp) DelHeader added in v0.5.0

func (h *DotResp) DelHeader(field string) string

DelHeader deletes a header field. It returns an empty string.

func (*DotResp) ReturnStatus added in v0.5.0

func (h *DotResp) ReturnStatus(status int) (string, error)

ReturnStatus sets the HTTP response status and exits template rendering immediately.

func (*DotResp) ServeContent added in v0.5.0

func (d *DotResp) ServeContent(path_ string, modtime time.Time, content any) (string, error)

ServeContent aborts execution of the template and instead responds to the request with content with any headers set by AddHeader and SetHeader so far but ignoring SetStatus.

func (*DotResp) SetHeader added in v0.5.0

func (h *DotResp) SetHeader(field, val string) string

SetHeader sets a header field value, overwriting any other values for that field. It returns an empty string.

func (*DotResp) SetStatus added in v0.5.0

func (h *DotResp) SetStatus(status int) string

SetStatus sets the HTTP response status. It returns an empty string.

type DotX added in v0.5.0

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

DotX is used as the field at .X in all template invocations.

func (DotX) Func added in v0.5.0

func (c DotX) Func(name string) any

Func returns a function by name to call manually. Can be used in combination with the call and try funcs.

func (DotX) StaticFileHash added in v0.5.0

func (d DotX) StaticFileHash(urlpath string) (string, error)

StaticFileHash returns the sha-384 hash of the named asset file to be used for integrity or caching behavior.

func (DotX) Template added in v0.5.0

func (c DotX) Template(name string, dot any) (template.HTML, error)

Template invokes the template name with the given dot value, returning the result as a html string.

type ErrorStatus added in v0.6.0

type ErrorStatus int

func (ErrorStatus) Error added in v0.6.0

func (e ErrorStatus) Error() string

type HandlerRoute added in v0.9.0

type HandlerRoute struct {
	// Pattern uses net/http.ServeMux syntax (e.g. "POST /foo/{bar}").
	Pattern string
	Handler http.Handler
}

HandlerRoute pairs a ServeMux pattern with an http.Handler.

type Instance added in v0.4.0

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

Instance is a configured, immutable, xtemplate request handler ready to execute templates and serve static files in response to http requests.

The only way to create a valid Instance is to call the Config.Instance method. Configuration of an Instance is intended to be immutable. Instead of mutating a running Instance, build a new Instance from a modified Config and swap them.

See also Server which manages instances and enables reloading them.

func (*Instance) Id added in v0.4.0

func (x *Instance) Id() int64

Id returns the id of this instance which is unique in the current process. This differentiates multiple instances, as the instance id is attached to all logs generated by the instance with the attribute name `xtemplate.instance`.

func (*Instance) ServeHTTP added in v0.5.0

func (instance *Instance) ServeHTTP(w http.ResponseWriter, r *http.Request)

type InstanceRoute added in v0.4.0

type InstanceRoute struct {
	Pattern string
	Handler http.Handler
}

type InstanceStats added in v0.4.0

type InstanceStats struct {
	Routes                        int
	TemplateFiles                 int
	TemplateDefinitions           int
	InitializationTemplates       int
	StaticFiles                   int
	StaticFilesAlternateEncodings int
}

type MarkdownDoc added in v0.9.2

type MarkdownDoc struct {
	Meta map[string]any `json:"meta,omitempty"`
	Body template.HTML  `json:"body,omitempty"`
}

MarkdownDoc is the result of rendering Markdown: the decoded front matter metadata and the rendered HTML body.

func FuncMarkdown added in v0.5.0

func FuncMarkdown(input any, configName ...string) (MarkdownDoc, error)

markdown parses front matter out of the input and renders the remaining Markdown to HTML in a single pass, returning both .Meta and .Body. input may be a string, []byte, or io.Reader. This uses the Goldmark library, which is CommonMark compliant. If an alternative markdown policy is not named, it uses the default policy which has these extensions enabled: YAML/TOML front matter, Github Flavored Markdown, Footnote, and syntax highlighting provided by Chroma.

type Option added in v0.6.0

type Option func(*Config) error

func WithFuncMaps added in v0.3.0

func WithFuncMaps(fm ...template.FuncMap) Option

func WithHandler added in v0.9.0

func WithHandler(pattern string, h http.Handler) Option

WithHandler mounts h at pattern on the instance ServeMux (net/http.ServeMux syntax, e.g. "POST /api/{id}" or "GET /healthz"). Appends to Config.Handlers.

Intended for embedding foreign handlers (API, webhooks, probes) as peer routes beside templates and static files.

func WithLogger added in v0.3.0

func WithLogger(logger *slog.Logger) Option

func WithProvider added in v0.5.0

func WithProvider(p DotConfig) Option

func WithTemplateFS added in v0.3.3

func WithTemplateFS(fs afero.Fs) Option

type Request added in v0.5.0

type Request struct {
	DotConfig
	ServerCtx context.Context
	W         http.ResponseWriter
	R         *http.Request
}

type ReturnError

type ReturnError struct{}

ReturnError is a sentinel value that indicates a successful/normal exit but causes template execution to stop immediately. Used by funcs and dot field methods to perform custom actions.

func (ReturnError) Error

func (ReturnError) Error() string

type Server added in v0.4.0

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

Server is a configured, *reloadable*, xtemplate request handler ready to execute templates and serve static files in response to http requests. It implements http.Handler by always routing to the current Instance.

Call Server.Reload to rebuild from the same config (or with options). If successful, Reload atomically swaps the old Instance for the new one so subsequent requests use the new instance; outstanding requests on the old Instance can finish. The old instance's Config.Ctx is cancelled.

The only way to create a valid *Server is to call Config.Server.

func (*Server) Instance added in v0.4.0

func (x *Server) Instance() *Instance

Instance returns the current Instance. After calling Reload, previous calls to Instance may be stale.

func (*Server) Reload added in v0.4.0

func (x *Server) Reload(cfgs ...Option) error

Reload creates a new Instance from the config and swaps it with the current instance if successful, otherwise returns the error.

func (*Server) Serve added in v0.4.0

func (x *Server) Serve(listen_addr string) error

Serve opens a net listener on `listen_addr` and serves requests from it. It returns when the listener fails or when Config.Ctx is cancelled, in which case the server is gracefully shut down and Serve returns nil.

func (*Server) ServeHTTP added in v0.10.0

func (x *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP routes the request to the current Instance, or responds 503 if the server has been stopped.

func (*Server) Stop added in v0.6.5

func (x *Server) Stop()

Directories

Path Synopsis
app
git
Package git serves an xtemplate site from a git repository, reusing app's config loading machinery.
Package git serves an xtemplate site from a git repository, reusing app's config loading machinery.
watchfs
watchfs reloads the server upon changes to the templates directory or other configured directories.
watchfs reloads the server upon changes to the templates directory or other configured directories.
standard
Package standard links the default set of dot-provider Caddyfile parsers (sql, fs, flags, nats), the pure-Go sqlite3 database/sql driver, and the xtemplate caddy module in a single opt-in import.
Package standard links the default set of dot-provider Caddyfile parsers (sql, fs, flags, nats), the pure-Go sqlite3 database/sql driver, and the xtemplate caddy module in a single opt-in import.
cmd
Basic xtemplate CLI package.
Basic xtemplate CLI package.
git command
Git-backed xtemplate CLI package.
Git-backed xtemplate CLI package.
watchfs command
The default xtemplate CLI package.
The default xtemplate CLI package.
examples
dotprovider command
Custom dot provider example: the "repository pattern".
Custom dot provider example: the "repository pattern".
embedded command
Embedded single-binary example: the templates dir is compiled into the binary via //go:embed, so this binary serves its templates with no templates dir on disk at runtime.
Embedded single-binary example: the templates dir is compiled into the binary via //go:embed, so this binary serves its templates with no templates dir on disk at runtime.
internal module
providers
dotflags/caddyfile
Package caddyfile registers the flags dot-provider for Caddyfile use.
Package caddyfile registers the flags dot-provider for Caddyfile use.
dotfs/caddyfile
Package caddyfile registers the fs dot-provider for Caddyfile use.
Package caddyfile registers the fs dot-provider for Caddyfile use.
dotnats/caddyfile
Package caddyfile registers the nats dot-provider for Caddyfile use.
Package caddyfile registers the nats dot-provider for Caddyfile use.
dotsql/caddyfile
Package caddyfile registers the sql dot-provider for Caddyfile use.
Package caddyfile registers the sql dot-provider for Caddyfile use.
nats module
register module
test module

Jump to

Keyboard shortcuts

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