Goa services can render dynamic HTML content using Go’s standard html/template
package. This guide shows you how to integrate template rendering into your Goa
service.
First, define the service endpoints that will render HTML templates:
package design
import . "goa.design/goa/v3/dsl"
var _ = Service("front", func() {
Description("Front-end web service with template rendering")
Method("home", func() {
Description("Render the home page")
Payload(func() {
Field(1, "name", String, "Name to display on homepage")
Required("name")
})
Result(Bytes)
HTTP(func() {
GET("/")
Response(StatusOK, func() {
ContentType("text/html")
})
})
})
})
Create a service that manages template rendering:
package front
import (
"context"
"embed"
"html/template"
"bytes"
"fmt"
genfront "myapp/gen/front" // replace with your generated package name
)
//go:embed templates/*.html
var templateFS embed.FS
type Service struct {
tmpl *template.Template
}
func New() (*Service, error) {
tmpl, err := template.ParseFS(templateFS, "templates/*.html")
if err != nil {
return nil, fmt.Errorf("failed to parse templates: %w", err)
}
return &Service{tmpl: tmpl}, nil
}
Implement the service methods to render templates:
func (svc *Service) Home(ctx context.Context, p *genfront.HomePayload) ([]byte, error) {
// Prepare data for the template
data := map[string]interface{}{
"Title": "Welcome",
"Content": "Welcome to " + p.Name + "!",
}
// Create a buffer to store the rendered template
var buf bytes.Buffer
// Render the template to the buffer
if err := svc.tmpl.ExecuteTemplate(&buf, "home.html", data); err != nil {
return nil, fmt.Errorf("failed to render template: %w", err)
}
return buf.Bytes(), nil
}
Create your HTML templates in the templates
directory:
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
</head>
<body>
{{block "content" .}}{{end}}
</body>
</html>
<!-- templates/home.html -->
{{template "base.html" .}}
{{define "content"}}
<h1>{{.Title}}</h1>
<p>{{.Content}}</p>
{{end}}
Set up your server with the template service:
func main() {
// Create the service
svc := front.New()
// Initialize HTTP server
endpoints := genfront.NewEndpoints(svc)
mux := goahttp.NewMuxer()
server := genserver.New(endpoints, mux, goahttp.RequestDecoder, goahttp.ResponseEncoder, nil, nil)
genserver.Mount(server, mux)
// Start the server
if err := http.ListenAndServe(":8080", mux); err != nil {
log.Fatalf("failed to start server: %v", err)
}
}
If you need to serve both static files and dynamic templates, you can combine them in your service design:
var _ = Service("front", func() {
// Dynamic template endpoints
Method("home", func() {
HTTP(func() {
GET("/")
Response(StatusOK, func() {
ContentType("text/html")
})
})
})
// Static file serving
Files("/static/{*filepath}", "public/static")
})
This setup serves:
Template Organization
Error Handling
Performance