Skip to main content

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 with http.HandleFunc in init().
  • owly.Files.Get, owly.Env, and owly.JSON/owly.Text cover 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 main under src/<name>/, mounted at its path from owly.yaml.