xtemplate

package module
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Jun 19, 2026 License: Apache-2.0 Imports: 57 Imported by: 6

README

xtemplate

xtemplate is a html/template-based hypertext preprocessor and rapid application development web server written in Go. Its good defaults handle typical server activity, enabling authors to focus on building hypermedia-exchange-oriented websites by defining routes and responding to them with template-generated HTML combined with configurable backing data stores.

🎯 Goal

Frustrated with the status-quo of frameworks that add more abstractions than they solve problems, I set out to create something that feels more like wrestling directly with first-class citizens of the web:

  • HTTP paths and matching server path patterns
  • Responding to an HTTP request with a template-generated HTML
  • Access to various backing data sources

🎇 The idea of xtemplate is that all of these can be managed with a directory of Go template files.

🚫 Anti-goals

xtemplate implements things that are required to make a good web server in a way that avoids common pitfalls with existing engines:

  • Rigid template behavior: Engines typically relegate templates to be dumb string concatenators with just enough dynamic behavior to walk over some known fixed data structure.
  • Inefficient template loading: Many engines often load template files from disk and parse them on every request, which is wasteful when web server definitions are largely static.
  • Constant rebuilds: Yet other engines rebuild the entire program from source when any little thing changes.
  • Unnecessary handler names: You've already had to name the http path and write the associated response template, why do you have to come up with a redundant name for the handler?
  • Default unsafe: Some engines require authors to vigilantly escape user inputs, risking XSS attacks that could have been avoided with less effort.
  • Inefficient asset serving: Many engines don't try to optimize serving assets at all and compress static assets at request time, instead of serving pre-compressed content with sendfile(2) and negotiated content encoding. Most designs don't give templates access to the hash of asset files, depriving authors of the right information to optimize cache behavior and check resource integrity.

✨ Features

Click a feature to expand and show details:

⚡ Efficient loading

All template files are read and parsed once, at startup, and kept in memory during the life of an xtemplate instance. Requests are routed to a handler that immediately starts executing a template reference in response. No slow cascading disk accesses or parsing overhead before you even begin crafting the response.

🔄 Live reload

Template files are loaded into a new instance and validated milliseconds after they are modified, no need to restart the server. If an error occurs during load the previous instance remains intact and continues to serve while the loading error is printed to the logs. A successful reload atomically swaps the handler so new requests are served by the new instance; pending requests are allowed to complete gracefully.

Add this template definition and one-line script to your page, then clients will automatically reload when the server does:

{{- define "SSE /reload"}}{{.Flush.WaitForServerStop}}data: reload{{printf "\n\n"}}{{end}}
<script>new EventSource("/reload").onmessage = () => location.reload()</script>
<!-- Maybe not a great idea for production, but you do you. -->
🗃️ Simple file-based routing

GET requests are handled by invoking a matching template file at that path. (Hidden files that start with . are loaded but not routed by default.)

File path:              HTTP path:
.
├── index.html          GET /
├── todos.html          GET /todos
├── admin
│   └── settings.html   GET /admin/settings
└── shared
    └── .head.html      (not routed because it starts with '.')
🔱 Add custom routes to handle any method and path pattern

Handle any Go 1.22 ServeMux pattern by defining a template with that pattern as its name. Path placeholders are available during template execution with the .Req.PathValue method.

<!-- match on path parameters -->
{{define "GET /contact/{id}"}}
{{$contact := .DB.QueryRow `SELECT name,phone FROM contacts WHERE id=?` (.Req.PathValue "id")}}
<div>
  <span>Name: {{$contact.name}}</span>
  <span>Phone: {{$contact.phone}}</span>
</div>
{{end}}

<!-- match on any http method -->
{{define "DELETE /contact/{id}"}}
{{$_ := .DB.Exec `DELETE from contacts WHERE id=?` (.Req.PathValue "id")}}
{{.Resp.SetStatus 204}}
{{end}}
👨‍💻 Define and invoke custom templates

All html files under the template root directory are available to invoke by their full path relative to the template root dir starting with /:

<html>
  <title>Home</title>
  <!-- import the contents of another file -->
  {{template "/shared/.head.html" .}}

  <body>
    <!-- invoke a custom named template defined anywhere -->
    {{template "navbar" .}}
    ...
  </body>
</html>
🛡️ XSS safe by default

The html/template library automatically escapes user content, so you can rest easy from basic XSS attacks. The defacto standard html sanitizer for Go, BlueMonday, is available for cases where you need finer grained control.

If you have some html string that you do trust, it's easy to inject if that's your intention with the trustHtml func.

🎨 Customize the context to provide selected data sources

Configure xtemplate to get access to built-in and custom data sources like running SQL queries against a database, sending and receiving messages using a message streaming client like NATS, read and list files from a local directory, reading static config from a key-value store, or perform any action you can define by writing a Go API, like the common "repository" design pattern for example.

Modify Config to add built-in or custom ContextProvider implementations, and they will be made available in the dot context.

Some built-in context providers are listed next:

💽 Database context provider: Execute queries

Add the built-in Database Context Provider to run queries using the configured Go driver and connection string for your database. (Supports the sqlite3 driver by default, compile with your desired driver to use it.)

<ul>
  {{range .DB.QueryRows `SELECT id,name FROM contacts`}}
  <li><a href="/contact/{{.id}}">{{.name}}</a></li>
  {{end}}
</ul>
🗄️ Filesystem context provider: List and read local files

Add the built-in Filesystem Context Provider to List and read files from the configured directory.

<p>Here are the files:
<ol>
{{range .FS.ReadDir "dir/"}}
  <li>{{.Name}}</li>
{{end}}
</ol>
💬 NATS context provider: Send and receive messages

Add and configure the NATS Context Provider to send messages, use the Request-Response pattern, and even send live updates to a client.

<example></example>
📤 Optimal asset serving

Non-template files in the templates directory are served directly from disk with appropriate caching responses, negotiating with the client to serve compressed encodings if corresponding .zst, .zip, .gz, .br files are present.

Templates can efficiently access static files' precalculated content hash to build a <script> or <link> integrity attribute, instructing clients to check the integrity of the content if they are served through a CDN. See: Subresource Integrity (SRI)

Add the content hash as a query parameter and responses will automatically add a 1 year long Cache-Control header so clients can safely cache as long as possible. If the file changes, its hash and thus query parameter will change and the client will immediately request a new version, completely eliminating stale cache issues.

This example uses both SRI and precise 1-year Cache-Control:

{{- with $hash := .X.StaticFileHash `/assets/reset.css`}}
<link rel="stylesheet" href="/reset.css?hash={{$hash}}" integrity="{{$hash}}">
{{- end}}
📬 Live updates with Server Sent Events (SSE)

Define a template with a name that starts with SSE, like SSE /url/path, and SSE requests will be handled by invoking the template. Individual messages can be sent by using .Flush, and the template can be paused to wait on messages sent over Go channels or can block on server shutdown.

🐜 Small footprint

Compiles to a ~30MB binary. Easily add your own custom functions and choice of database driver on top.

🏃‍♂️‍➡️ Single binary deployments

Deploy next to your templates and static files or embed them for single binary deployments.

//go:embed all:templates
var Files embed.FS

📦 How to run

0. 📦 As a Docker container

...

1. 📦 As a Caddy plugin

The xtemplate/caddy plugin offers all xtemplate features integrated into Caddy, a fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS.

Download Caddy with xtemplate/caddy middleware plugin built-in:

https://caddyserver.com/download?package=github.com%2Finfogulch%2Fxtemplate&package=github.com%2Fncruces%2Fgo-sqlite3

This is the simplest Caddyfile that uses the xtemplate/caddy plugin:

routes {
  xtemplate
}

Alternatively, build with the xcaddy CLI tool.

2. 📦 As the default CLI application

Download from the Releases page or build the binary in ./cmd.

Custom builds can include your chosen db drivers, make Go functions available to the template definitions, and even embed templates for true single binary deployments. The ./cmd package is the reference CLI application, consider starting your customization there.

🎏 CLI flags and examples: (click to show)
$ ./xtemplate -h
v0.8.3
Usage: xtemplate [--template-dir TEMPLATE-DIR] [--template-ext TEMPLATE-EXT] [--minify] [--ldelim LDELIM] [--rdelim RDELIM] [--watch WATCH] [--watchtemplates] [--listen LISTEN] [--loglevel LOGLEVEL] [--config CONFIG] [--config-file CONFIG-FILE]

Options:
  --template-dir TEMPLATE-DIR, -t TEMPLATE-DIR [default: templates]
  --template-ext TEMPLATE-EXT [default: .html]
  --minify, -m [default: true]
  --ldelim LDELIM [default: {{]
  --rdelim RDELIM [default: }}]
  --watch WATCH
  --watchtemplates [default: true]
  --listen LISTEN, -l LISTEN [default: 0.0.0.0:8080]
  --loglevel LOGLEVEL [default: -2]
  --config CONFIG, -c CONFIG
  --config-file CONFIG-FILE, -f CONFIG-FILE
  --help, -h             display this help and exit
  --version              display version and exit

Examples:
    Listen on port 80:
    $ ./xtemplate --listen :80

    Specify a context directory and reload when it changes:
    $ ./xtemplate --template-dir public --watch-templates

    Parse template files matching a custom extension and minify them:
    $ ./xtemplate --template-ext ".go.html" --minify
3. 📦 As a Go library

Go Reference

xtemplate's public Go API starts with a xtemplate.Config, from which you can get either an xtemplate.Instance interface or a xtemplate.Server interface, with the methods config.Instance() and config.Server(), respectively.

An xtemplate.Instance is an immutable http.Handler that can handle requests, and exposes some metadata about the files loaded as well as the ServeMux patterns and associated handlers for individual routes. An xtemplate.Server also handles http requests by forwarding requests to an internal Instance, but the Server can be reloaded by calling server.Reload(), which creates a new Instance with the previous config and atomically switches the handler to direct new requests to the new Instance.

Use an Instance if you have no interest in reloading, or if you want to use xtemplate handlers in your own mux. Use a Server if you want an easy way to smoothly reload and replace the xtemplate Instance behind a http.Handler at runtime.

👨‍🏭 How to use

🧰 Template semantics

xtemplate templates are based on Go's html/template package, with some additional features and enhancements. Here are the key things to keep in mind:

  • All template files are loaded recursively from the specified root directory, and they are parsed and cached in memory at startup.
  • Each template file is associated with a specific route based on its file path. For example, index.html in the root directory will handle requests to the / path, while admin/settings.html will handle requests to /admin/settings.
  • You can define custom routes by defining a template with a special name in your template files. For example, {{define "GET /custom-route"}}...{{end}} will create a new route that handles GET requests to /custom-route. Names also support path parameters as defined by http.ServeMux.
  • Template files can be invoked from within other templates using either their full path relative to the template root or by using its defined template name.
  • Templates are executed with a uniform context object, which provides access to request data, database connections, and other useful dynamic functionality.
  • Templates can also call functions set at startup.

[!note]

Custom dot fields and functions are similar in that they both add functionality to the templates, but dot fields are distinguished in that they are initialized on every request with access to request-scoped details including the underlying http.Request and http.ResponseWriter objects, the request-scoped logger, and the server context.

Thus FuncMap functions are recommended for adding simple computational functionality (like parsing, escaping, data structure manipulation, etc), whereas dot fields are recommended for more complicated tasks like accessing network resources, running database queries, accessing the file system, etc.

📝 Context

The dot context {{.}} set on each template invocation provides access to request-specific data and response control methods, and can be modified to add custom fields with your own methods.

✏️ Built-in dot fields

These fields are always present in relevant template invocations:

  • Access instance data with the .X field. See DotX
  • Access request details with the .Req field. See DotReq
  • Control the HTTP response in buffered template handlers with the .Resp field. See DotResp
  • Control flushing behavior for flushing template handlers (i.e. SSE) with the .Flush field. See DotFlush
✏️ Optional dot fields

These optional value providers can be configured with any field name, and can be configured multiple times with different configurations.

  • Read and list files. See [DotFS]
  • Query and execute SQL statements. See DotDB
  • Read template-level key-value map. See DotKV
✏️ Custom dot fields

You can create custom dot fields that expose arbitrary Go functionality to your templates. See 👩‍⚕️ Writing a custom DotProvider.

📐 Functions

These are built-in functions that are available to all invocations and don't depend on request context or mutate state. There are three sets by default: functions that come by default in the go template library, functions from the sprig library, and custom functions added by xtemplate.

You can custom FuncMaps by configuring the Config.FuncMaps field.

  • 📏 xtemplate includes funcs to render markdown, sanitize html, convert values to human-readable forms, and to try to call a function to handle an error within the template. See the free functions named FuncXYZ(...) in xtemplate's Go docs for details.
  • 📏 Sprig publishes a library of useful template funcs that enable templates to manipulate strings, integers, floating point numbers, and dates, as well as perform encoding tasks, manipulate lists and dicts, converting types, and manipulate file paths See Sprig Function Documentation.
  • 📏 Go's built in functions add logic and basic printing functionality. See: text/template#Functions.

🏆 Users

👷‍♀️ Development

🗺️ Repository structure

xtemplate is split into the following packages:

  • github.com/infogulch/xtemplate is a library that exports the Instance struct which can load template files and implements http.Handler that routes requests to templates and serves static files, the Server struct which can atomically reload an Instance on demand, and a number of built-in providers.
  • ./app is a library that contains an exported Main function which configures and starts xtemplate with CLI args and accepts config override parameters. This Main fucntion can be used as a reference for using the xtemplate API in advanced use-cases.
  • ./cmd is a binary that simply imports a database driver and runs xtemplate/app.Main(). The recommended way to begin customizing xtemplate is to copy the ./cmd package to your own repo, then add your own database driver, provide custom config overrides, etc.
  • ./caddy is a Caddy module package that uses xtemplate's Go library API to integrate xtemplate into Caddy server.

[!TIP]

To understand how the xtemplate package works, it may be helpful to skim through the files in this order: config.go, server.go instance.go, build.go, handlers.go.

Testing

Tasks are managed with mise. The task definitions live as Nushell scripts under .config/mise/tasks (sharing helpers from .config/mise/lib.nu) and the tool versions (Go, hurl, Nushell, xcaddy) are pinned in .config/mise/config.toml, so local and CI runs use identical versions. List the available tasks with mise tasks. Each task is a standalone Nushell script, so it can also be run directly (e.g. ./.config/mise/tasks/gotest) from anywhere in the repo without going through mise run.

The integration tests run xtemplate configured to use test/templates as the templates dir and test/context as the FS dot provider, then run the hurl files from the test/tests directory against the running server. The same suite is exercised against all three deployment targets:

  • mise run test-cli builds and tests the standalone CLI binary.
  • mise run test-caddy builds and tests the Caddy module.
  • mise run test-docker builds and tests the Docker image.

mise run ci runs the whole pipeline: lint (lint-nu parse-checks the task scripts with nu --ide-check, lint-go runs golangci-lint), go test (gotest), the three integration targets, and then the release dist and Docker image builds.

👩‍⚕️ Writing a custom DotProvider

Implement the xtemplate.DotConfig interface on your type:

type DotConfig interface {
    FieldName() string            // the dot field name, e.g. "Shop" for {{.Shop}}
    Init(context.Context) error   // called once at instance load
    Value(Request) (any, error)   // called per request; its return is assigned to the dot field
}

Register it by passing xtemplate.WithProvider(yourConfig) as an option to config.Server(...), config.Instance(...), or app.Main(...):

app.Main(xtemplate.WithProvider(&ShopConfig{}))

On startup xtemplate creates a struct that includes your value as a field named by FieldName(). For every request your Value method is called with request details and its return value is assigned onto that struct, which is passed to html/template as the dot value {{.}}. Value must return a stable, non-nil concrete type: it is called once with a mock request at load time to infer the field type via reflection.

Optionally implement xtemplate.CleanupDotProvider to run per-request cleanup (e.g. rolling back a transaction), the way the built-in DotDB provider does.

See examples/dotprovider for a complete, runnable example.

✅ Project history and license

The idea for this project started as infogulch/go-htmx (now archived), which included the first implementations of template-name-based routing, exposing sql db functions to templates, and a persistent templates instance shared across requests and reloaded when template files changed.

go-htmx was refactored and rebased on top of the templates module from the Caddy server to create caddy-xtemplate to add some extra features including reading files directly and built-in funcs for markdown conversion, and to get a jump start on supporting the broad array of web server features without having to implement them from scratch.

xtemplate has since been refactored to be usable independently from Caddy, and is published as a subpackage in this module at ./caddy which uses the public xtemplate Go API and to integrate xtemplate into Caddy as an http middleware.

See CHANGELOG.md for a per-release history of notable changes.

xtemplate is 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, available to 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 FuncMarkdown added in v0.5.0

func FuncMarkdown(input string, configName ...string) (template.HTML, error)

markdown renders the given Markdown text as HTML and returns it. 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: Github Flavored Markdown, Footnote, and syntax highlighting provided by Chroma.

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 FuncSplitFrontMatter added in v0.5.0

func FuncSplitFrontMatter(input string) (parsedMarkdownDoc, error)

splitFrontMatter parses front matter out from the beginning of input, and returns the separated key-value pairs and the body/content. input must be a "stringy" value.

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 FuncWithArgs added in v0.9.0

func FuncWithArgs(dot any, args ...any) (any, error)

withArgs attaches extra arguments to the dot value so they can be passed to a sub-template, working around Go's restriction that the {{template}} action accepts only a single data argument. It returns a wrapper struct that embeds the original dot (so all of its fields and methods remain accessible) and exposes the extra arguments as a .Args slice. For example:

{{template "head" withArgs . "Post not found"}}

and inside the "head" template:

<title>{{.Args | idx 0}}</title>
{{.X.StaticFileHash "/assets/style.css"}}

Calling withArgs on an already-wrapped dot replaces .Args rather than nesting, keeping the embedded dot flat. The dot must be a struct.

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

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 so that Defaults() can distinguish "unset" (nil, which
	// becomes true) from an explicit false. A plain bool could not: Server and
	// Instance re-apply Defaults internally, so a zero-valued false would be
	// indistinguishable from unset and could never be honored, and the
	// documented default of true would only hold when constructing via New().
	Minify *bool `json:"minify,omitempty" arg:"-m,--minify" default:"true"`

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

	Databases       []DotDBConfig    `json:"databases" arg:"-"`
	Flags           []DotFlagsConfig `json:"flags" arg:"-"`
	Directories     []DotDirConfig   `json:"directories" arg:"-"`
	Nats            []DotNatsConfig  `json:"nats" arg:"-"`
	CustomProviders []DotConfig      `json:"-" arg:"-"`

	// 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:"-"`

	// Custom HTTP handlers, mounted on the router alongside template and static
	// file routes. Registered on every instance, 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 Dir added in v0.8.0

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

Dir

func (Dir) Chroot added in v0.9.0

func (d Dir) Chroot(path string) (Dir, error)

Chroot returns a copy of the filesystem with root changed to path.

func (Dir) Exists added in v0.8.0

func (d Dir) Exists(filename string) bool

Exists returns true if filename can be opened successfully.

func (Dir) ExistsDir added in v0.9.0

func (d Dir) ExistsDir(dirname string) bool

func (Dir) Open added in v0.8.0

func (d Dir) Open(filename string) (afero.File, error)

Open opens the file

func (Dir) Read added in v0.8.0

func (d Dir) Read(name string) (string, error)

Read returns the contents of a filename relative to the FS root as a string.

func (Dir) ReadDir added in v0.9.0

func (d Dir) ReadDir(path string) ([]fs.FileInfo, error)

ReadDir reads the directory named by dirname and returns a list of directory entries sorted by filename.

func (Dir) Stat added in v0.8.0

func (d Dir) Stat(filename string) (fs.FileInfo, error)

Stat returns Stat of a filename.

Note: if you intend to read the file, afterwards, calling .Open instead may be more efficient.

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 DotDB added in v0.8.0

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

DotDB is used to create a dot field value that can query a SQL database. When any of its statement executing methods are called, it creates a new transaction. When template execution finishes, if there were no errors it automatically commits any uncommitted transactions remaining after template execution completes, but if there were errors then it calls rollback on the transaction.

func (*DotDB) Commit added in v0.8.0

func (c *DotDB) Commit() (string, error)

Commit manually commits any implicit transactions opened by this DotDB. This is called automatically if there were no errors at the end of template execution.

func (*DotDB) Exec added in v0.8.0

func (c *DotDB) Exec(query string, params ...any) (result sql.Result, err error)

Exec executes a statement with parameters and returns the raw sql.Result. Note: this can be a bit difficult to use inside a template, consider using other methods that provide easier to use return values.

func (*DotDB) QueryRow added in v0.8.0

func (c *DotDB) QueryRow(query string, params ...any) (map[string]any, error)

QueryRow executes a query, which must return one row, and returns it as a map[string]any.

func (*DotDB) QueryRows added in v0.8.0

func (c *DotDB) QueryRows(query string, params ...any) (iter.Seq[map[string]any], error)

QueryRows executes a query and returns an iterator that yields one map[string]any per result row. Rows are scanned lazily as the sequence is consumed instead of being buffered up front, so a `{{range}}` over the result only holds a single row in memory at a time.

Errors that occur before iteration starts (opening the transaction, executing the query, reading column metadata) are returned directly. Errors that occur while scanning rows can't be returned from the iterator, so they are raised via panic(template.ExecError{...}); the template engine recovers these and returns them from template execution, aborting it just like a normal error return would.

func (*DotDB) QueryVal added in v0.8.0

func (c *DotDB) QueryVal(query string, params ...any) (any, error)

QueryVal executes a query, which must return one row with one column, and returns the value of the column.

func (*DotDB) Rollback added in v0.8.0

func (c *DotDB) Rollback() (string, error)

Rollback manually rolls back any implicit transactions opened by this DotDB. This is called automatically if there were any errors that occurred during template exeuction.

type DotDBConfig added in v0.8.0

type DotDBConfig struct {
	*sql.DB        `json:"-"`
	*sql.TxOptions `json:"-"`
	Name           string `json:"name"`
	Driver         string `json:"driver"`
	Connstr        string `json:"connstr"`
	MaxOpenConns   int    `json:"max_open_conns"`
}

func (*DotDBConfig) Cleanup added in v0.8.0

func (dp *DotDBConfig) Cleanup(v any, err error) error

func (*DotDBConfig) FieldName added in v0.8.0

func (d *DotDBConfig) FieldName() string

func (*DotDBConfig) Init added in v0.8.0

func (d *DotDBConfig) Init(ctx context.Context) error

func (*DotDBConfig) Value added in v0.8.0

func (d *DotDBConfig) Value(r Request) (any, error)

type DotDirConfig added in v0.8.0

type DotDirConfig struct {
	Name string   `json:"name"`
	Path string   `json:"path"`
	Type string   `json:"type"`
	FS   afero.Fs `json:"-"`
}

DotDirConfig can configure an xtemplate dot field to provide file system access to templates. You can configure xtemplate to use it three ways:

By setting a cli flag: “

func (*DotDirConfig) Cleanup added in v0.8.0

func (p *DotDirConfig) Cleanup(a any, err error) error

func (*DotDirConfig) FieldName added in v0.8.0

func (c *DotDirConfig) FieldName() string

func (*DotDirConfig) Init added in v0.8.0

func (p *DotDirConfig) Init(ctx context.Context) error

func (*DotDirConfig) Value added in v0.8.0

func (p *DotDirConfig) Value(r Request) (any, error)

type DotFlags added in v0.8.0

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

func (DotFlags) Value added in v0.8.0

func (d DotFlags) Value(key string) string

type DotFlagsConfig added in v0.8.0

type DotFlagsConfig struct {
	Name   string            `json:"name"`
	Values map[string]string `json:"values"`
}

func (*DotFlagsConfig) FieldName added in v0.8.0

func (d *DotFlagsConfig) FieldName() string

func (*DotFlagsConfig) Init added in v0.8.0

func (d *DotFlagsConfig) Init(_ context.Context) error

func (*DotFlagsConfig) Value added in v0.8.0

func (d *DotFlagsConfig) 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 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 millisecionds.

func (*DotFlush) WaitForServerStop added in v0.5.0

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

WaitForServerStop blocks execution until the request is canceled by the client or until the server closes.

type DotKV added in v0.8.0

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

func (*DotKV) Delete added in v0.8.0

func (d *DotKV) Delete(key string) error

func (*DotKV) Get added in v0.8.0

func (d *DotKV) Get(key string) (string, error)

func (*DotKV) Purge added in v0.8.0

func (d *DotKV) Purge(key string) error

func (*DotKV) Put added in v0.8.0

func (d *DotKV) Put(key, value string) error

func (*DotKV) Watch added in v0.8.0

func (d *DotKV) Watch(keys string) (<-chan jetstream.KeyValueEntry, error)

type DotNats added in v0.8.0

type DotNats struct {
	*nats.Conn
	jetstream.JetStream
	// contains filtered or unexported fields
}

func (*DotNats) Publish added in v0.8.0

func (d *DotNats) Publish(subject, message string) error

func (*DotNats) Request added in v0.8.0

func (d *DotNats) Request(subject, data string, timeout_ ...time.Duration) (*nats.Msg, error)

func (*DotNats) Subscribe added in v0.8.0

func (d *DotNats) Subscribe(subject string) (<-chan *nats.Msg, error)

type DotNatsConfig added in v0.8.0

type DotNatsConfig struct {
	Name string `json:"name"`

	*NatsConfig `json:"nats_config"`
	Conn        *nats.Conn
	// contains filtered or unexported fields
}

func (*DotNatsConfig) FieldName added in v0.8.0

func (d *DotNatsConfig) FieldName() string

func (*DotNatsConfig) Init added in v0.8.0

func (d *DotNatsConfig) Init(ctx context.Context) error

func (*DotNatsConfig) Value added in v0.8.0

func (d *DotNatsConfig) Value(r Request) (any, error)

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
	TemplateInitializers          int
	StaticFiles                   int
	StaticFilesAlternateEncodings int
}

type NatsConfig added in v0.8.0

type NatsConfig struct {
	InProcessServerOptions *server.Options          `json:"in_process_server_options"`
	ConnOptions            *nats.Options            `json:"conn_options"`
	JetStreamOptions       []jetstream.JetStreamOpt // encode jetstream opts into json?
}

type Option added in v0.6.0

type Option func(*Config) error

func WithDB added in v0.3.0

func WithDB(name string, db *sql.DB, opt *sql.TxOptions) Option

func WithDir added in v0.8.0

func WithDir(name string, fs afero.Fs) Option

WithDir creates an xtemplate.Option that can be used with xtemplate.Config.Server, xtemplate.Config.Instance, or xtemplate.Main to add an fs dot provider to the config.

func WithFlags added in v0.8.0

func WithFlags(name string, flags map[string]string) Option

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 adds a custom HTTP handler at pattern (net/http.ServeMux syntax, e.g. "POST /hook").

func WithLogger added in v0.3.0

func WithLogger(logger *slog.Logger) Option

func WithNats added in v0.8.0

func WithNats(name string, serverOpts *server.Options, connOpts *nats.Options, jsOpts []jetstream.JetStreamOpt) 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 manages an Instance and allows you to reload template files with the same config by calling `server.Reload()`. If successful, Reload atomically swaps the old Instance with the new Instance so subsequent requests are handled by the new instance, and any outstanding requests still being served by the old Instance can continue to completion. The old instance's Config.Ctx is also cancelled.

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

func (*Server) Handler added in v0.4.0

func (x *Server) Handler() http.Handler

Handler returns a `http.Handler` that always routes new requests to the current Instance.

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.

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.
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 DotProvider example: the "repository pattern".
Custom DotProvider 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
nats module
register module
test module

Jump to

Keyboard shortcuts

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