A Go function, end to end
A worked example: an endpoint that renders an HTML page from a template file,
showing net/http routing, reading a static file, env vars, and the response
helpers. It builds on the sdk-go/owly reference.
Scaffold
owly init --lang go portal
cd portal
owly add --lang go page --path /page
The handler
src/page/main.go:
package main
import (
"net/http"
"strings"
"github.com/owly/owly-testing/sdk-go/owly"
)
func init() {
http.HandleFunc("/page", func(w http.ResponseWriter, r *http.Request) {
// Read the template from the project's public directory, with a fallback.
template := "<h1>{{TITLE}}</h1><p>v{{VERSION}}</p>"
if b, err := owly.Files.Get("page.html"); err == nil {
template = string(b)
}
title := r.URL.Query().Get("title")
if title == "" {
title = "Portal"
}
html := strings.NewReplacer(
"{{TITLE}}", title,
"{{VERSION}}", owly.Env("APP_VERSION", "1.0.0"),
).Replace(template)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Write([]byte(html))
})
}
func main() {}
Set the env var in owly.yaml:
functions:
- name: page
lang: go
entrypoint: src/page/main.go
path: /page
env:
APP_VERSION: "2.3.0"
A JSON API in the same project
owly add --lang go compute --path /compute, then src/compute/main.go:
package main
import (
"net/http"
"strconv"
"time"
"github.com/owly/owly-testing/sdk-go/owly"
)
func init() {
http.HandleFunc("/compute", func(w http.ResponseWriter, r *http.Request) {
n := 100_000
if v := r.URL.Query().Get("n"); v != "" {
if parsed, err := strconv.Atoi(v); err == nil && parsed > 0 {
n = parsed
}
}
start := time.Now()
sum := 0
for i := 0; i < n; i++ {
sum += i % 7
}
owly.JSON(w, http.StatusOK, map[string]any{
"iterations": n,
"checksum": sum,
"duration_ms": time.Since(start).Milliseconds(),
})
})
}
func main() {}
Run it
owly build
owly deploy
curl 'https://portal.<host>/page?title=Welcome'
curl 'https://portal.<host>/compute?n=500000'
What to take from this
- Stay
net/http-native: register routes withhttp.HandleFuncininit(). owly.Files.Get,owly.Env, andowly.JSON/owly.Textcover files, config, and responses without boilerplate.- Handlers are request-scoped — read what you need per request (here the template) rather than relying on cross-request state. See Cold starts.
- Each function is its own
package mainundersrc/<name>/, mounted at itspathfromowly.yaml.