This repository contains two Golang templates for OpenFaaS which give additional control over the HTTP request and response. They will both handle higher throughput than the classic watchdog due to the process being kept warm.
Our recommended template for Go developers is golang-middleware.
You'll find a chapter dedicated to writing functions with Go in Everyday Golang by Alex Ellis
Using the templates:
faas-cli template store pull golang-http
faas-cli template store pull golang-middleware
$ faas template pull https://github.com/openfaas/golang-http-template
$ faas new --list
Languages available as templates:
- golang-http
- golang-middleware
The two templates are very similar:
golang-middleware
implements a
http.HandleFunc
from Go's stdlib.
golang-http
uses a structured request/response object
You can manage dependencies in one of the following ways:
GO111MODULE=on
but you also can make that explicit by adding
--build-arg GO111MODULE=on
to
faas-cli up
, you can also use
--build-arg GOPROXY=https://
if you want to use your own mirror for the modules
go mod vendor
in your function folder and add
--build-arg GO111MODULE=off --build-arg GOFLAGS='-mod=vendor'
to
faas-cli up
If you do not wish to, or cannot use vendoring for some reason, then we provide an alternative set of templates for OpenFaaS Pro customers:
This is one of the fastest templates available for Go available. Its signature is a http.HandlerFunc , instead of a traditional request and response that you may expect from a function.
The user has complete control over the HTTP request and response.
$ faas template store pull golang-middleware
$ faas template pull https://github.com/openfaas/golang-http-template
$ faas new --lang golang-middleware <fn-name>
Example writing a JSON response:
package function
import (
"encoding/json"
"fmt"
"net/http"
func Handle(w http.ResponseWriter, r *http.Request) {
var input []byte
if r.Body != nil {
defer r.Body.Close()
// read request payload
reqBody, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
input = reqBody
// log to stdout
fmt.Printf("request body: %s", string(input
))
response := struct {
Payload string `json:"payload"`
Headers map[string][]string `json:"headers"`
Environment []string `json:"environment"`
Payload: string(input),
Headers: r.Header,
Environment: os.Environ(),
resBody, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
// write result
w.WriteHeader(http.StatusOK)
w.Write(resBody)
Example persistent database connection pool between function calls:
package function
import (
"database/sql"
"fmt"
"net/http"
"strings"
_ "github.com/go-sql-driver/mysql"
// db pool shared between function calls
var db *sql.DB
func init() {
var err error
db, err = sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err.Error())
err = db.Ping()
if err != nil {
panic(err.Error())
func Handle(w http.ResponseWriter, r *http.Request) {
var query string
ctx := r.Context()
if r.Body != nil {
defer r.Body.Close()
// read request payload
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
query = string(body)
// log to stdout
fmt.Printf("Executing query: %s", query)
rows, err := db.QueryContext(ctx, query)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
defer rows.Close()
ids := make([]string, 0)
for rows.Next() {
if e := ctx.Err(); e != nil {
http.Error(w, e, http.StatusBadRequest)
return
var id int
if err := rows.Scan(&id); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
ids = append(ids, string(id))
if err := rows.Err(); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
result := fmt.Sprintf("ids %s", strings.Join(ids, ", "))
// write result
w.WriteHeader(http.StatusOK)
w.Write([]byte(result))
Example retrieving request query strings
package function
import (
"fmt"
"net/http"
func Handle(w http.ResponseWriter, r *http.Request) {
// Parses RawQuery and returns the corresponding
// values as a map[string][]string
// for more info https://golang.org/pkg/net/url/#URL.Query
query := r.URL.Query()
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf("id: %s", query.Get("id"))))
If a folder named static
is found in the root of your function's source code, it will be copied into the final image published for your function.
To read this back at runtime, you can do the following:
package function
import (
"net/http"
func Handle(w http.ResponseWriter, r *http.Request) {
data, err := os.ReadFile("./static/file.txt")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
w.Write(data)
This template provides additional context and control over the HTTP response from your function.
Like the golang-middleware
template, this template is highly performant and suitable for production.
$ faas template store pull golang-http
$ faas template pull https://github.com/openfaas/golang-http-template
$ faas new --lang golang-http <fn-name>
Example writing a successful message:
package function
import (
"fmt"
"net/http"
handler "github.com/openfaas/templates-sdk/go-http"
// Handle a function invocation
func Handle(req handler.Request) (handler.Response, error) {
var err error
message := fmt.Sprintf("Hello world, input was: %s", string(req.Body))
return handler.Response{
Body: []byte(message),
}, err
Example writing a custom status code
package function
import (
"fmt"
"net/http"
handler "github.com/openfaas/templates-sdk/go-http"
// Handle a function invocation
func Handle(req handler.Request) (handler.Response, error) {
var err error
return handler.Response{
Body: []byte("Your workload was accepted"),
StatusCode: http.StatusAccepted,
}, err
Example writing an error / failure.
package function
import (
"fmt"
"net/http"
handler "github.com/openfaas/templates-sdk/go-http"
// Handle a function invocation
func Handle(req handler.Request) (handler.Response, error) {
var err error
return handler.Response{
Body: []byte("the input was invalid")
}, fmt.Errorf("invalid input")
The error will be logged to stderr
and the body
will be written to the client along with a HTTP 500 status code.
Example reading a header.
package function
import (
"log"
handler "github.com/openfaas/templates-sdk/go-http"
// Handle a function invocation
func Handle(req handler.Request) (handler.Response, error) {
var err error
log.Println(req.Header) // Check function logs for the request headers
return handler.Response{
Body: []byte("This is the response"),
Header: map[string][]string{
"X-Served-By": []string{"My Awesome Function"},
}, err
Example responding to an aborted request.
The Request
object provides access to the request context. This allows you to check if the request has been cancelled by using the context's done channel req.Context().Done()
or the context's error req.Context().Err()
package function
import (
"fmt"
"net/http"
handler "github.com/openfaas/templates-sdk/go-http"
// Handle a function invocation
func Handle(req handler.Request) (handler.Response, error) {
var err error
for i := 0; i < 10000; i++ {
if req.Context().Err() != nil {
return handler.Response{}, fmt.Errorf("request cancelled")
fmt.Printf("count %d\n", i)
message := fmt.Sprintf("Hello world, input was: %s", string(req.Body))
return handler.Response{
Body: []byte(message),
StatusCode: http.StatusOK,
}, err
This context can also be passed to other methods so that they can respond to the cancellation as well, for example db.ExecContext(req.Context())
It is often natural to organize your code into sub-packages, for example you may have a function with the following structure
./echo
├── go.mod
├── go.sum
├── handler.go
└── pkg
└── version
└── version.go
Now if you want to reference theversion
sub-package, import it as
import "handler/function/pkg/version"
This works like any local Go project.
Sub-modules (meaning sub-folders with a go.mod
) are not supported.