Go WASM Nodes
Go can compile to WASM using TinyGo (recommended for smaller binaries) or standard Go.
Prerequisites
Section titled “Prerequisites”# Install TinyGo (recommended)# macOSbrew install tinygo
# Linuxwget https://github.com/tinygo-org/tinygo/releases/download/v0.32.0/tinygo_0.32.0_amd64.debsudo dpkg -i tinygo_0.32.0_amd64.deb
# Verify installationtinygo versionProject Setup
Section titled “Project Setup”mkdir my-custom-nodecd my-custom-nodego mod init my-custom-nodeTemplate Code
Section titled “Template Code”package main
import ( "encoding/json" "strings" "unsafe")
// Node definition structurestype NodeDefinition struct { Name string `json:"name"` FriendlyName string `json:"friendly_name"` Description string `json:"description"` Category string `json:"category"` Icon string `json:"icon,omitempty"` Pins []PinDefinition `json:"pins"` Scores *NodeScores `json:"scores,omitempty"`}
type PinDefinition struct { Name string `json:"name"` FriendlyName string `json:"friendly_name"` Description string `json:"description"` PinType string `json:"pin_type"` DataType string `json:"data_type"` DefaultValue interface{} `json:"default_value,omitempty"`}
type NodeScores struct { Privacy uint8 `json:"privacy"` Security uint8 `json:"security"` Performance uint8 `json:"performance"` Governance uint8 `json:"governance"` Reliability uint8 `json:"reliability"` Cost uint8 `json:"cost"`}
// Execution contexttype ExecutionContext struct { Inputs map[string]interface{} `json:"inputs"`}
type ExecutionResult struct { Outputs map[string]interface{} `json:"outputs"` Error string `json:"error,omitempty"`}
// Memory management for WASMvar resultBuffer []byte
//export get_nodefunc get_node() unsafe.Pointer { node := NodeDefinition{ Name: "wasm_go_uppercase", FriendlyName: "Uppercase (Go)", Description: "Converts a string to uppercase using Go", Category: "Custom/Text", Icon: "/flow/icons/text.svg", Pins: []PinDefinition{ { Name: "exec_in", FriendlyName: "▶", Description: "Trigger execution", PinType: "Input", DataType: "Execution", }, { Name: "exec_out", FriendlyName: "▶", Description: "Continue execution", PinType: "Output", DataType: "Execution", }, { Name: "input", FriendlyName: "Input", Description: "The string to convert", PinType: "Input", DataType: "String", DefaultValue: "", }, { Name: "output", FriendlyName: "Output", Description: "The uppercase string", PinType: "Output", DataType: "String", }, }, Scores: &NodeScores{ Privacy: 0, Security: 0, Performance: 1, Governance: 0, Reliability: 0, Cost: 0, }, }
jsonBytes, _ := json.Marshal(node) resultBuffer = jsonBytes return unsafe.Pointer(&resultBuffer[0])}
//export runfunc run(contextPtr unsafe.Pointer, contextLen uint32) unsafe.Pointer { // Read context from memory contextBytes := unsafe.Slice((*byte)(contextPtr), contextLen)
var context ExecutionContext if err := json.Unmarshal(contextBytes, &context); err != nil { return errorResult("Failed to parse context: " + err.Error()) }
// Get input value input := "" if val, ok := context.Inputs["input"]; ok { if str, ok := val.(string); ok { input = str } }
// Execute logic output := strings.ToUpper(input)
// Return result result := ExecutionResult{ Outputs: map[string]interface{}{ "output": output, }, }
jsonBytes, _ := json.Marshal(result) resultBuffer = jsonBytes return unsafe.Pointer(&resultBuffer[0])}
func errorResult(message string) unsafe.Pointer { result := ExecutionResult{ Outputs: make(map[string]interface{}), Error: message, } jsonBytes, _ := json.Marshal(result) resultBuffer = jsonBytes return unsafe.Pointer(&resultBuffer[0])}
func main() {}Build with TinyGo
Section titled “Build with TinyGo”tinygo build -o my-custom-node.wasm -target=wasip1 -opt=s main.goBuild Options
Section titled “Build Options”| Flag | Description |
|---|---|
-target=wasip1 | WASI Preview 1 target |
-opt=s | Optimize for size |
-opt=2 | Optimize for speed |
-no-debug | Remove debug info |
Build with Standard Go
Section titled “Build with Standard Go”Standard Go produces larger binaries but has full language support:
GOOS=wasip1 GOARCH=wasm go build -o my-custom-node.wasm main.goSize Comparison
Section titled “Size Comparison”| Compiler | Typical Size |
|---|---|
TinyGo -opt=s | ~50-200 KB |
| Standard Go | ~2-5 MB |
Install
Section titled “Install”cp my-custom-node.wasm ~/.flow-like/nodes/Limitations (TinyGo)
Section titled “Limitations (TinyGo)”TinyGo doesn’t support all Go features:
- ❌
reflect(limited support) - ❌
cgo - ❌ Some stdlib packages
- ✅ Most common packages work
See TinyGo compatibility for details.
Advanced: Using Third-Party Packages
Section titled “Advanced: Using Third-Party Packages”import ( "github.com/google/uuid")
func generateID() string { return uuid.New().String()}Build:
go get github.com/google/uuidtinygo build -o node.wasm -target=wasip1 main.go