Run Extism WebAssembly plugins from a Go application

How to write a Host Application

For a few days, we have seen that it is possible to develop WebAssembly plugins with the Extism Plugin Development Kit and run them with the Extism CLI. Today, it's time to move up a level: we will create an application in Go that can load and run these plugins as the CLI does.

We will use the Host SDK of Extism for the Go language to do this. As a reminder, Extism provides Host SDKs for many languages (https://extism.org/docs/category/integrate-into-your-codebase).

As a reminder, a host application is an application that, thanks to a Wasm runtime SDK, can run WebAssembly programs. The Host SDKs of Extism are "overlays" on the Wasm runtime SDK to make your life easier (avoid complicated plumbing).

Currently, Extism uses the WasmTime runtime.

If I refer to this issue (WASI threads support), the support of other Wasm runtimes may be taken into account and in particular Wazero.

But enough talk; let's get down to business.

Prerequisites

You will need

✋ Pay attention (Mon 7 Aug 2023)

The Extism Go SDK on top of Wazero. Now, the SDK is purely written in Go (no more dependency on cgo 🎉). However, the new SDK is not released yet. So to be able to use it and build your projects, you must:

  • clone the new repository: git clone git@github.com:extism/go-sdk.git

  • add a local reference of this git repository into the go.mod file of your projects: replace github.com/extism/extism => ../go-sdk

https://twitter.com/mhmd_azeez/status/1688198055986622464

Creating the application

Start by creating a go.mod file with the command go mod init go-host-application, then a main.go file with the following content:

package main

import (
    "context"
    "fmt"
    "github.com/extism/extism"
    "github.com/tetratelabs/wazero"
)

func main() {

    ctx := context.Background()

    // Define config, path to the wasm file 
    // and manifest 0️⃣
    config := extism.PluginConfig{
        ModuleConfig: wazero.NewModuleConfig().WithSysWalltime(),
        EnableWasi:   true,
    }
️
    path := "../03-even-with-javascript/hello-js.wasm"

    // Define the path to the wasm file 1️⃣
    manifest := extism.Manifest{
        Wasm: []extism.Wasm{
            extism.WasmFile{
                Path: path},
        }}

    // Load the wasm plugin 2️⃣
    pluginInst, err := extism.NewPlugin(ctx, manifest, config, nil)

    if err != nil {
        panic(err)
    }

    // Call the `say_hello` function 3️⃣
    // with a string parameter
    _, res, err := pluginInst.Call(
        "say_hello",
        []byte("👋 Hello from the Go Host app 🤗"),
    )

    if err != nil {
        fmt.Println("😡", err)
    } else {
        // Display the return value 4️⃣
        fmt.Println("🙂", string(res))
    }

}

You see, the code is straightforward:

  • 0: let's use the JavaScript Wasm plugin we developed in the previous article.

  • 1: define a manifest with properties, including the Wasm file path.

  • 2: load the Wasm plugin.

  • 3: call the say_hello function of the plugin.

  • 4: display the result (the type of res is []byte).

Run the program

Use simply this command:

go run main.go

And you will get this:

🙂 param: 👋 Hello from the Go Host app 🤗

You can do the test with the first plugin developed with TinyGo. Change the value of the variable path := "../01-simple-go-plugin/simple.wasm" and run again:

go run main.go

And you should get this:

🙂 👋 Hello 👋 Hello from the Go Host app 🤗

🎉 you see, creating Go applications that run Wasm plugins written in different languages is easy.

If I can keep up the pace, tomorrow I'll explain how to do the same thing but this time with Node.js.