Template storage service for the Gas ecosystem. Provides multiple gas.TemplateProvider
implementations — in-memory, filesystem, database, and composite — for storing and retrieving raw template content.
go get github.com/gasmod/gas-template| Backend | Package | Use case |
|---|---|---|
| Memory | github.com/gasmod/gas-template/memory |
Development, testing, ephemeral storage |
| Directory | github.com/gasmod/gas-template/dir |
Static templates on disk with runtime overlay |
| FS | github.com/gasmod/gas-template/fs |
Read-only adapter for fs.FS (e.g. embed.FS) |
| Database | github.com/gasmod/gas-template/db |
Persistent, multi-instance deployments (Pg/MySQL/SQLite) |
| Composite | github.com/gasmod/gas-template/composite |
Chain multiple providers with fallback reads |
Memory, directory, fs, and composite stores implement gas.TemplateProvider.
The database store also implements gas.Service (with DI, migrations, and lifecycle management).
import "github.com/gasmod/gas-template/memory"
store := memory.NewStore()
if err := store.Register(ctx, "emails/welcome.html", []byte("<h1>Welcome</h1>")); err != nil {
// handle error
}
content, err := store.Get(ctx, "emails/welcome.html")import "github.com/gasmod/gas-template/dir"
// NewStore returns a DI-injectable constructor; call it to get a *Store.
store := dir.NewStore("./templates")()
defer store.Close()
// Reads from disk; overlay takes precedence.
content, err := store.Get(ctx, "home.html")
// Programmatic additions go to the in-memory overlay.
_ = store.Register(ctx, "dynamic.html", []byte("<p>Dynamic</p>"))Read-only adapter for any fs.FS — most commonly an embed.FS. Register
and RegisterFS return template.ErrReadOnly; wrap in a composite store
with a writable provider for mutability.
import (
"embed"
tmplfs "github.com/gasmod/gas-template/fs"
)
//go:embed templates/*.html
var templateFS embed.FS
// NewStore returns a DI-injectable constructor; call it to get a *Store.
store := tmplfs.NewStore(templateFS)()
content, err := store.Get(ctx, "templates/home.html")package main
import (
"github.com/gasmod/gas"
database "github.com/gasmod/gas-database"
migrate "github.com/gasmod/gas-migrate"
tmpldb "github.com/gasmod/gas-template/db"
)
func main() {
app := gas.NewApp(
gas.WithSingletonService[*database.Service](database.New()),
gas.WithSingletonService[*migrate.Service](migrate.New()),
gas.WithSingletonService[*tmpldb.Store](tmpldb.NewStore()),
// ...
)
app.Run()
}With a custom namespace:
tmpldb.NewStore(tmpldb.WithNamespace("emails"))Chain multiple providers — writes go to the first, reads fall back through all:
import (
"github.com/gasmod/gas-template/composite"
"github.com/gasmod/gas-template/memory"
"github.com/gasmod/gas-template/dir"
)
writable := memory.NewStore()
disk := dir.NewStore("./templates")()
defer disk.Close()
store := composite.NewStore(writable, disk)
// Get checks writable first, then disk.
content, err := store.Get(ctx, "page.html")
// Register goes to the writable provider only.
_ = store.Register(ctx, "override.html", []byte("<p>Override</p>"))Services receive templates through gas.TemplateProvider via constructor injection:
type Service struct {
templates gas.TemplateProvider
}
func New(templates gas.TemplateProvider) *Service {
return &Service{templates: templates}
}
func (s *Service) Init() error {
content, err := s.templates.Get(context.Background(), "emails/welcome.html")
if err != nil {
return err
}
// use content...
return nil
}All writable stores (memory, dir, db) support loading templates from an
fs.FS via RegisterFS:
import "embed"
//go:embed templates/*.html
var templateFS embed.FS
if err := store.RegisterFS(ctx, templateFS); err != nil {
// handle error
}Only .html files are registered; other extensions are skipped. For a
read-only view over an fs.FS without copying contents into another
store, use the fs backend instead.
The db package supports three database dialects. The correct dialect is selected
automatically based on the configured database driver:
| Driver | Dialect |
|---|---|
postgres, pgx |
PostgreSQL |
mysql |
MySQL |
sqlite, sqlite3 |
SQLite |
The templates table migration is registered automatically with gas-migrate during Init().
Multiple db.Store instances can share the same table by using different namespaces:
gas.WithSingletonService[*tmpldb.Store](tmpldb.NewStore(tmpldb.WithNamespace("emails")))The default namespace is "default".
The database store exposes two additional methods beyond gas.TemplateProvider:
store.Exists("page.html") // (bool, error)
store.Delete("page.html") // error — returns template.ErrTemplateNotFound if missingThe templatetest package provides a mock implementation of gas.TemplateProvider:
import "github.com/gasmod/gas-template/templatetest"
mock := &templatetest.MockTemplate{}
mock.GetFn = func(ctx context.Context, name string) ([]byte, error) {
return []byte("<h1>Hello</h1>"), nil
}
// pass mock as gas.TemplateProvider
// assert calls:
if mock.CallCount("Get") != 1 {
t.Error("expected one Get call")
}The root template package defines a sentinel error used by all backends:
template.ErrTemplateNotFound // returned by Get when the template does not exist
template.IsNotFound(err) // helper to check if an error is ErrTemplateNotFound