Scale Functions currently support using Golang as both a Guest and the Host language.

This means that you can write your Functions in Golang (Guest Support) and you can run Functions written in any supported language inside a Golang application (Host Support).

Guest Support

A Scale Function written in Golang often looks something like this:

scale.go
//go:build tinygo || js || wasm
package scale

import (
   signature "github.com/loopholelabs/scale-signature-http"
)

func Scale(ctx *signature.Context) (*signature.Context, error) {
   ctx.Response().SetBody("Hello, World!")
   return ctx.Next()
}

The above example is a simple HTTP Function that responds with “Hello, World!” to any request.

By default, the generated function will be configured to use the HTTP Signature, which provides an interface for handling HTTP requests and responses. To learn more about the HTTP Signature and the interfaces it provides, you can check out the HTTP Signature Reference. Learn more about Signatures in the Signatures Overview.

Compiling these Functions requires the appropriate toolchain to be installed on your machine.

Installing the Golang Toolchain

To use Golang as the Guest Language, you'll need to install the following:

The best way to install Go is to follow the official instructions for your operating system and platform at https://go.dev/doc/install.

You can verify that Go was installed correctly by running the following command:

go version

The minimum supported version of Go for use with Scale Functions is 1.18.

The best way to install TinyGo is to follow the official instructions for your operating system and platform at https://tinygo.org/getting-started/install/.

You can verify that TinyGo was installed correctly by running the following command:

tinygo version

The minimum supported version of TinyGo for use with Scale Functions is 0.27.0.

Host Support

To use a compiled Scale Function (written in any supported language) inside a Golang application, there are 3 steps:

  1. Import the Scale Function(s) into the Golang application by embedding them
  2. Create a new Scale Runtime and pass in the Scale Function(s)
  3. Call the Run method on the Scale Runtime with your request

Importing Scale Functions

Scale Functions can be imported into a Golang application by embedding them Directly.

Embedding Scale Functions

Scale Functions can be embedded into a Golang application by using the //go:embed directive, after it has been exported using the scale fn export command (see Exporting Scale Functions).

import (
	"embed"
	"github.com/loopholelabs/scale/scalefunc"
)

//go:embed <path to exported function>
var embeddedFunction []byte

func main() {
    // ...
    sf := new(ScaleFunc)
	_ = sf.Decode(embeddedFunction)
    // ...
}

Creating a Scale Runtime

Once you’ve imported your Scale Function(s) into your Golang application, you can create a new instance of the Scale Runtime and pass in the Scale Function(s).

import (
	"context"
	scale "github.com/loopholelabs/scale"
)

func main() {
    // ...
	ctx := context.Background()
    r := scale.New(ctx, sf) // Or scale.New(ctx, sf1, sf2, sf3, ...)
    // ...
}

Running a Scale Function

Once you’ve created the Scale Runtime, you can call the Instance method on it to get a new runtime instance. The Context method of the Instance can then be used to set the request body, and the Run method can be used to run the Scale Function (or Functions).

import (
    "context"
)

func main() {
    // ...
    i, _ := r.Instance()
    i.Context().Request.Body = []byte("Hello, World!")

    ctx := context.Background()
    _ = i.Run(ctx)
    // ...
}

You’ll notice that we’re not passing anything into to the r.Instance method above. In actually, it is possible to pass in one or multiple native golang functions (with the signature (ctx *signature.Context) (*signature.Context, error)) to be run as part of the Scale Function chain.

The Instance method will automatically pass the result of the previous function to the next one (and pass the responses back up through the chain).

If you’re not using a native handler (or handlers) at the end of the chain, you can don’t need to pass anything into the r.Instance method.

It’s important to note that an Instance can be reused multiple times, but it’s not thread-safe. If you need to run multiple instances of a Scale Function concurrently, you’ll need to create a new Instance for each one.

Instances are heavily optimized and will recycle themselves automatically when they’re no longer in use. This means that creating instances are very cheap and you can create as many as you need as often as you need.

The Instance method itself is thread-safe and can be called concurrently.

To remove existing instances from a runtime, simply call Clear() on the runtime itself:

scale.go
import (
    "context"
    scale "github.com/loopholelabs/scale"
)

func main() {
    // ...
    r := scale.New(ctx, sf)
    // ...
    ctx := context.Background()
    _ = i.Run(ctx)
    // ...
    r.Clear
}

Putting it all Together

Here’s a complete example of a Golang application that imports a Scale Function, creates a new Scale Runtime, and runs it.

scale.go
package main

import (
    "context"
    "embed"
    "fmt"
    scale "github.com/loopholelabs/scale"
)

//go:embed hello-latest.scale
var embeddedFunction []byte

func main() {
    sf := new(scale.ScaleFunc)
    _ = sf.Decode(embeddedFunction)

    ctx := context.Background()
    r := scale.New(ctx, sf)

    i, _ := r.Instance()
    i.Context().Request.Body = []byte("Hello, World!")

    _ = i.Run(ctx)

    fmt.Println(string(i.Context().Response.Body))
}

What’s Next?

Now that you’ve learned how to use Scale Functions inside your Golang application, you should check out our pre-made adapters for popular Golang frameworks and libraries, such as net/http and FastHTTP.