Welcome to the new Golem Cloud Docs! 👋
Documentation
Go Language Guide
Defining Components

Defining Golem Components in Go

Creating a project

Using Golem's command line interface provides a set of predefined, Golem-specific examples to choose from as a starting point.

To see the available examples for Go, run:

$ golem-cli list-examples --language go

The set of examples provided by Golem CLI is defined in the open-source repository golem-examples (opens in a new tab).

Then to create a new project based on one of these examples, run:

$ golem-cli new --example go-actor-full --component-name go-example --package-name 'golem:demo'

The command will create a new Golem project in the go-example directory, and print short, language-specific instructions on how to build the project.

Specification-first approach

Golem and the Go toolchain currently requires defining the component's interface using the WebAssembly Interface Type (WIT) format. See the official documentation of this format (opens in a new tab) for reference.

Each new project generated with golem-cli contains a wit directory with at least one .wit file defining a world. This world can contain exports (exported functions and interfaces) and these exports will be the compiled Golem component's public API.

The first time a component is compiled (see the Building Components page for details), a couple of files get generated in a subdirectory named as the your component. This go module contains the definitions of all the data types and interfaces defined in the WIT file(s).

To implement the specification written in WIT, the Go code must implement some of these generated interfaces and set them explicitly in the init function.

Exporting top-level functions

WIT allows exporting one or more top-level functions in the world section, for example:

package golem:demo;
 
world go-example {
    export hello-world: func() -> string;
}

To implement this function in Go, the following steps must be taken:

  • make sure the generated golem/demo/go_example module is imported
  • define an empty struct representing our component
  • implement the generated GoExample interface for this struct
  • call the SetGoExample function to install the implementation

Let's see in code:

package main
 
// make sure the generated module is imported
import (
	"golem/demo/go_example"
)
 
// define an empty struct representing our component
type GoExampleImpl struct {
}
 
// implement the generated `Guest` trait for this struct
func (e GoExampleImpl) HelloWorld() string {
	return "Hello, World!"
}
 
// install the implementation
func init() {
	a := GoExampleImpl{}
	go_example.SetGoExample(a)
}
 
⚠️

Note that in WIT, identifiers are using the kebab-case naming convention, while Go uses the PascalCase convention. The generated bindings map between the two automatically.

Exporting interfaces

WIT supports defining and exporting whole interfaces, coupling together multiple functions and possibly custom data types.

Take the following example:

package golem:demo;
 
interface api {
  add: func(value: u64);
  get: func() -> u64;
}
 
world example {
  export api;
}

This is similar to having the two exported functions directly exported from the world section, but there is a corresponding Go interface generated that needs to be separately implemented and installed:

package main
 
import (
	"golem/demo/go_example"
)
 
type GoExampleImpl struct {
}
 
// total State can be stored in global variables
var total uint64
 
func (e GoExampleImpl) Add(value uint64) {
	total += value
}
 
func (e GoExampleImpl) Get() uint64 {
	return total
}
 
func init() {
	a := GoExampleImpl{}
	go_example.SetExportsGolemDemoApi(a)
}

See the Managing state section below to learn the recommended way of managing state in Golem components, which is required to implement these two functions.

Exporting resources

The WIT format supports defining and exporting resources - entities defined by their constructor function and the available methods on them.

Golem supports exporting these resources as part of the worker's API.

The following example modifies the previously seen counter example to define it as a resource, getting the counter's name as a constructor parameter:

package golem:demo;
 
interface api {
 
  resource counter {
    constructor(name: string);
    add: func(value: u64);
    get: func() -> u64;
  }
}
 
world example {
  export api;
}

Resources can have multiple instances within a worker. Their constructor returns a handle which is then used to call the methods on the resource. Learn more about how resources can be implicitly created and invoked through Golem's APIs in the Invocations page.

To implement the above defined WIT resource in Go a few new steps must be taken:

  • define a struct representing the resource - it can contain data!
  • implement the interface generated as the resource's interface for this struct
  • implement the constructor on the component struct

Let's see in code:

package main
 
import (
	"golem/demo/go_example"
)
 
// struct representing the component
type GoExampleImpl struct {
}
 
// struct representing an instance of the resource
type Counter struct {
	name    string
	current uint64
}
 
func (e GoExampleImpl) ConstructorCounter(name string) go_example.ExportsGolemDemoApiCounter {
	return Counter{
		name:    name,
		current: 0,
	}
}
 
func (e Counter) MethodCounterAdd(value uint64) {
	e.current += value
}
 
func (e Counter) MethodCounterGet() uint64 {
	return e.current
}
 
func init() {
	a := GoExampleImpl{}
	go_example.SetExportsGolemDemoApi(a)
}

Data types defined in WIT

The WIT specifications contains some primitive and higher level data types and also allows defining custom data types which can be used as function parameters and return values on the exported functions, interfaces and resources.

The following table shows an example of each WIT data type and its corresponding Rust type:

WIT typeRust type
boolbool
s8, s16, s32, s64int8, int16, int32, int64
u8, u16, u32, u64uint8, uint16, uint32, uint64
f32, f64float32, float64
charrune
stringstring
list<string>[]string
option<u64>Option[uint64]
result<s32, string>Result[int32, string] where Result is defined in the binding module
result<_, string>Result[struct{}, String]
resultResult[struct{}, struct{}]
tuple<u64, string, char>Generated struct with a name describing the types like GoExampleTuple3U64StringByteT

WIT records

The following WIT record type:

package golem:demo;
 
interface api {
    record user {
        id: u64;
        name: string;
    }
}

Will generate the following Go struct:

type ExportsGolemDemoApiUser struct {
	Id uint64,
	Name string
}

WIT variants

The following WIT variant type:

package golem:demo;
 
interface api {
    variant color {
        red,
        green,
        blue,
        rgb(u32)
    }
}

Will generate the following Go enum:

type ExportsGolemDemoApiColorKind int
 
const (
ExportsGolemDemoApiColorKindRed ExportsGolemDemoApiColorKind = iota
ExportsGolemDemoApiColorKindGreen
ExportsGolemDemoApiColorKindBlue
ExportsGolemDemoApiColorKindRgb
)
 
type ExportsGolemDemoApiColor struct {
  kind ExportsGolemDemoApiColorKind
  val any
}

as well as a set of helper functions.

WIT enums

The following WIT enum type:

package golem:demo;
 
interface api {
    enum color {
        red,
        green,
        blue
    }
}

Will generate the following Go enum:

type ExportsGolemDemoApiColorKind int
 
const (
ExportsGolemDemoApiColorKindRed ExportsGolemDemoApiColorKind = iota
ExportsGolemDemoApiColorKindGreen
ExportsGolemDemoApiColorKindBlue
)
 
type ExportsGolemDemoApiColor struct {
  kind ExportsGolemDemoApiColorKind
}

and a set of helper functions.

WIT flags

The following WIT flags type:

package golem:demo;
 
interface api {
    flags access {
        read,
        write,
        lst
    }
}

Will generate the following Go bitflags:

type ExportsGolemDemoApiAccess uint64
const (
ExportsGolemDemoApiAccess_Read ExportsGolemDemoApiAccess = 1 << iota
ExportsGolemDemoApiAccess_Write
ExportsGolemDemoApiAccess_Lst
)

Worker configuration

It is often required to pass configuration values to workers when they are started.

In general Golem supports three different ways of doing this:

  1. Defining a list of string arguments passed to the worker, available as command line arguments
  2. Defining a list of key-value pairs passed to the worker, available as environment variables.
  3. Using resource constructors to pass configuration values to the worker.

Command line arguments

⚠️

Currently TinyGo does not support the standard os.Args function, so we need to use the Golem GO SDK to get the command line arguments.

First, make sure the Golem Go SDK is set up for the project.

Then you can use the golem_go_bindings package to get the command line arguments:

args := golem_go_bindings.WasiCli0_2_0_EnvironmentGetArguments()

Environment variables

⚠️

Currently TinyGo does not support the standard os package's environment functions, so we need to use the Golem GO SDK to get the command line arguments.

First, make sure the Golem Go SDK is set up for the project.

Then you can use the golem_go_bindings package to get the command line arguments:

env := golem_go_bindings.WasiCli0_2_0_EnvironmentGetEnvironment()
for _, pair := range env {
	fmt.Println(pair.F0, " = ", pair.F1)
}

Resource constructors

As explained earlier, Golem workers can export resources and these resources can have constructor parameters.

Although resources can be used in many ways, one pattern for Golem is only create a single instance of the exported resource in each worker, and use it to pass configuration values to the worker. This is supported by Golem's worker invocation syntax directly, allowing to implicitly create workers and the corresponding resource by a single invocation as described on the Invocations page.

Managing state

Golem workers are stateful. There are two major techniques to store and manipulate state in a Golem worker implemented in Rust:

  1. Using a global variable
  2. Using resources and storing state in the resource's struct

Both techniques were demonstrated above in the code examples.