Templating / MVC

GoNest supports server-side HTML rendering using Go’s html/template. This enables traditional MVC patterns alongside your API routes. Equivalent to NestJS view engine integration.

Setup

Create a template engine and register it with the application:

engine, err := gonest.NewGoTemplateEngine(gonest.GoTemplateEngineOptions{
    Dir:       "./views",
    Extension: ".html", // default
})
if err != nil {
    log.Fatal(err)
}

app := gonest.Create(AppModule)
app.SetViewEngine(engine)

From Embedded Files

Use embed.FS for self-contained binaries:

//go:embed views/*.html
var viewsFS embed.FS

engine, err := gonest.NewGoTemplateEngineFromFS(viewsFS, "views/*.html")

Custom Template Functions

engine, err := gonest.NewGoTemplateEngine(gonest.GoTemplateEngineOptions{
    Dir: "./views",
    FuncMap: template.FuncMap{
        "upper": strings.ToUpper,
        "formatDate": func(t time.Time) string {
            return t.Format("Jan 2, 2006")
        },
    },
})

Rendering Templates

Use gonest.Render in your handlers:

func (c *HomeController) Register(r gonest.Router) {
    r.Get("/", c.index)
    r.Get("/about", c.about)
}

func (c *HomeController) index(ctx gonest.Context) error {
    return gonest.Render(ctx, "index.html", map[string]any{
        "Title":    "Home",
        "Articles": c.service.GetRecent(),
    })
}

func (c *HomeController) about(ctx gonest.Context) error {
    return gonest.Render(ctx, "about.html", map[string]any{
        "Title": "About Us",
    })
}

The response Content-Type is automatically set to text/html; charset=utf-8.

RenderInterceptor

For automatic template rendering based on route metadata, use RenderInterceptor:

app.UseGlobalInterceptors(gonest.NewRenderInterceptor(engine))

// In controller -- set "render" metadata to the template name
r.Get("/dashboard", c.dashboard).SetMetadata("render", "dashboard.html")

The interceptor renders the template using the handler’s return value as template data.

TemplateEngine (Alternative)

GoNest also provides a simpler TemplateEngine with lazy template loading:

engine := gonest.NewTemplateEngine("./views")
engine.SetExtension(".html")
engine.AddFunc("upper", strings.ToUpper)

// Use RenderHandler for a concise route definition
r.Get("/", gonest.RenderHandler(engine, "index", func(ctx gonest.Context) any {
    return map[string]any{"Title": "Home"}
}))

Static Files

Serve static assets (CSS, JS, images) from a directory:

func (c *Controller) Register(r gonest.Router) {
    r.Get("/static/*", gonest.StaticFiles("/static", "./public"))
}

This serves files from ./public under the /static URL prefix. For example, ./public/style.css is served at /static/style.css.

Example Template

<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}}</title>
</head>
<body>
    <h1>{{.Title}}</h1>
    {{range .Articles}}
    <article>
        <h2>{{.Name}}</h2>
        <p>{{.Summary}}</p>
    </article>
    {{end}}
</body>
</html>

See example/21-mvc for a complete working example.