Skip to content

Basic Go Application


  1. Start by running heim new to create a project from a template.



    Use the up/down arrow keys to select the template and press enter:

    Heim Cli
    heim@system:~/repos$ heim new
    ? Select template: ›
    c-http
    csharp-http
    csharp-http-async
    ...
    go-http
    ...
    python-http
    rust-http
    typescript-http
    openapi-specification



    Next, we will choose the HTTP method that the application will respond on.


    For this we will choose the default value of GET:

    Heim Cli
    Select template: · go-http
    ? Http trigger method POST/GET, Default (GET) ›



    Next, we need to choose the path that the application will listen on.

    Once the trigger path is set, we’ll be able to go to the trigger path, and use the HTTP method we set to trigger the application.


    Heim Cli
    Select template: · go-http
    Http trigger method POST/GET, Default · GET
    ? Http trigger path /??? , Default (/hello) › /myapp


    Last, we will need to set the name of the application.

    Heim Cli
    Select template: · go-http
    Http trigger method POST/GET, Default · GET
    Http trigger path /??? , Default · /myapp
    ? Package name, (Required) › myapp
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\network\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stdout\\stdout.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\filesystem\\types\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\.gitignore""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\ip-name-lookup\\ip-name-lookup.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\tcp\\tcp.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\tcp\\tcp.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stdin\\stdin.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stderr\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\outgoing-handler\\outgoing-handler.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\insecure\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\component.toml""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\streams\\streams.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\tcp-create-socket\\tcp-create-socket.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stdout\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\timezone\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\random\\random.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-output\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\udp\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stdin\\terminal-stdin.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\go.mod""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stdin\\terminal-stdin.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\ip-name-lookup\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\run\\run.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stderr\\stderr.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\poll\\poll.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-output\\terminal-output.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\outgoing-handler\\outgoing-handler.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\run\\run.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\poll\\poll.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\incoming-handler\\incoming-handler.exports.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\ip-name-lookup\\ip-name-lookup.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\tcp-create-socket\\tcp-create-socket.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\environment\\environment.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\types\\types.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\types\\types.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\error\\error.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\udp\\udp.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\udp-create-socket\\udp-create-socket.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\udp-create-socket\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\environment\\environment.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\udp\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wit\\proxy.wit""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\monotonic-clock\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-input\\terminal-input.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\filesystem\\types\\types.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\incoming-handler\\incoming-handler.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\udp\\udp.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\tcp\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\filesystem\\preopens\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stdout\\stdout.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\incoming-handler\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\monotonic-clock\\monotonic-clock.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stderr\\stderr.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stderr\\terminal-stderr.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\filesystem\\types\\types.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\streams\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\random\\random.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\go.sum""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\insecure-seed\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stdout\\terminal-stdout.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\wall-clock\\wall-clock.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\insecure-seed\\insecure-seed.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\insecure-seed\\insecure-seed.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\run\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\types\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\application.toml""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\timezone\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\instance-network\\instance-network.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\environment\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\error\\error.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\wall-clock\\wall-clock.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\instance-network\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stdout\\terminal-stdout.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\main.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stdout\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stderr\\terminal-stderr.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\timezone\\timezone.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\insecure\\insecure.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\filesystem\\preopens\\preopens.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\instance-network\\instance-network.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\timezone\\timezone.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\network\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\network\\network.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\tcp\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\tcp-create-socket\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-output\\terminal-output.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\outgoing-handler\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\outgoing-handler\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stderr\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-stdin\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\udp-create-socket\\udp-create-socket.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stdin\\stdin.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\filesystem\\types\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\filesystem\\preopens\\preopens.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\incoming-handler\\incoming-handler.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\http\\types\\abi.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\poll\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\stdin\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-input\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\cli\\terminal-input\\terminal-input.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\streams\\streams.wasm.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\monotonic-clock\\monotonic-clock.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\clocks\\wall-clock\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\insecure\\insecure.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\random\\random\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\ip-name-lookup\\empty.s""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\sockets\\network\\network.wit.go""
    Rendered Template: ""\\Temp\\heimZLXfGX\\myapp\\wasi\\io\\error\\empty.s""
    [00:00:00] ######################################## 110/110 Success

    Once we’ve gone through all the previous steps, we’ll have a project ready to go.





  2. Let’s open the project in your text editor to check on what our new application looks like…


    Now, you will have an application folder with this structure:

    • Directorywasi/
    • Directorywit/
    • .gitignore
    • application.toml
    • component.toml
    • go.mod
    • go.sum
    • main.go

    Take note of the marked configuration files. Let’s take a look at these files, next.



    The component.toml file, defines a component, and one or more components make up an application in Heim.


    component.toml

    name = "myapp"
    version = "0.1.0"
    [build.dev]
    build = "tinygo build -target=wasip2 --wit-package ./wit/proxy.wit --wit-world nor2:heim/nor2-world -o bin/myapp.wasm main.go"
    wasm_path = "bin/myapp.wasm"
    [build.prod]
    build = "tinygo build -target=wasip2 --wit-package ./wit/proxy.wit --wit-world nor2:heim/nor2-world -gc=conservative -opt=z -o bin/myapp.wasm main.go"
    wasm_path = "bin/myapp.wasm"

    a simple component.toml, with explanatory comments




    The application.toml is what defines an application. A application.toml is required for deploying an application to Heim.


    application.toml

    name = "myapp"
    component_target = 'myapp:0.1.0'
    [[trigger]]
    type = 'http'
    path = '/myapp'
    method = 'GET'

    a simple application.toml, with explanatory comments






  3. Open main.go and take a look at the code we have:

    main.go

    package main
    import (
    incominghandler "go-http/wasi/http/incoming-handler"
    "go-http/wasi/http/types"
    "net/http"
    "go.bytecodealliance.org/cm"
    )
    func init() {
    incominghandler.Exports.Handle = Handle
    }
    func main() {}
    func Handle(request types.IncomingRequest, responseOutParam types.ResponseOutparam) {
    // Set headers
    headers := types.NewFields()
    header := []byte("text/plain")
    _, _, isErr := headers.Append("Content-Type", types.FieldValue(cm.NewList(&header[0], len(header)))).Result()
    if isErr {
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Failed to append headers"))))
    return
    }
    // Create response
    response := types.NewOutgoingResponse(headers)
    response.SetStatusCode(types.StatusCode(http.StatusOK))
    // Write and finalize body
    body, _, isErr := response.Body().Result()
    if isErr {
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Body was read multiple times"))))
    return
    }
    bodyStream, _, isErr := body.Write().Result()
    if isErr {
    body.ResourceDrop()
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Body stream was read multiple times"))))
    return
    }
    bodyContent := []byte("Hello world!")
    _, _, isErr = bodyStream.Write(cm.NewList(&bodyContent[0], len(bodyContent))).Result()
    if isErr {
    body.ResourceDrop()
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Attempted to write to a closed stream"))))
    return
    }
    bodyStream.ResourceDrop()
    _, _, isErr = types.OutgoingBodyFinish(body, cm.None[types.Trailers]()).Result()
    if isErr {
    body.ResourceDrop()
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Attempted to write to a closed stream"))))
    return
    }
    // Send the response
    types.ResponseOutparamSet(responseOutParam, cm.OK[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](response))
    }

    The main.go file contains boilerplate source code.



    Now, we want to start the application. Read on to learn the steps needed to start an application.



    1. Run heim start, to start the heim runtime:

      heim runtime
      heim@system:~/repos$ heim start
      Starting Runtime with Config:
      ...
      Http server listening on: http://127.0.0.1:3000
      Heim portal: http://127.0.0.1:3000/heim/portal/

      Note that the Heim portal comes included with any Heim installation, and that it will be available on http://127.0.0.1:3000/heim/portal/ once you’ve installed Heim.





    2. To build the application and deploy it to our local-instance, we will run heim deploy. By default, this will create a debug build, but we can change it to a release build with the --release flag:

      heim cli
      heim@system:~/repos$ heim deploy
      INFO [ Heim::Deploy ] Building Component: myapp
      INFO [ heim_registry::registry ] "myapp":"0.1.0":d34c64efacf7d7aff4136c6d177f2ef97ec3326a0bd61d77221cad122d097284
      INFO [ Heim::Deploy ] Heim application `myapp` is now built and written to registry.
      INFO [ Heim::Pipeline ] Starting pipeline 'Deploy application'
      INFO [ Heim::Pipeline ] Storing unoptimized component state
      INFO [ Heim::Pipeline ] Checking if defined regions in app definition is valid
      INFO [ Heim::Pipeline ] Continuing with localhost region heim-localhost
      INFO [ Heim::Pipeline ] Getting region specific application environment values
      INFO [ Heim::Pipeline ] Set environment values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Getting region specific application scaling values
      INFO [ Heim::Pipeline ] Set scaling values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Getting capabilities
      INFO [ Heim::Pipeline ] Set capabilities values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Getting triggers
      INFO [ Heim::Pipeline ] Set trigger values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Component environment values are valid for all regions
      INFO [ Heim::Pipeline ] Checking for region with changes to build
      INFO [ Heim::Pipeline ] Region "heim-localhost" has been changed and will be built
      INFO [ Heim::Pipeline ] Storing component and optimized component
      INFO [ Heim::Pipeline ] Stored component at ''
      INFO [ Heim::Pipeline ] Stored optimized component at ''
      INFO [ Heim::Pipeline ] Storing region states
      INFO [ Heim::Pipeline ] Stored state for region(s) heim-localhost successfully
      INFO [ Heim::Pipeline ] Creating and caching application(s)
      INFO [ Heim::Pipeline ] Creating and caching application(s) for region(s) heim-localhost
      INFO [ Heim::Pipeline ] Created application with hash c75e711a22ed212cb5fca17d70848974cd540dfd9229697f84d759e22481569f for region(s) heim-localhost
      INFO [ Heim::Pipeline ] Application has been deployed for region(s) heim-localhost
      INFO [ Heim::Pipeline ] Pipeline 'Deploy application' finished with status 'Success'
      INFO [ Heim::Deploy ] Heim deploy pipeline was successful
      INFO [ Heim::Deploy ] Application is accaccessible at:
      INFO [ Heim::Deploy ] [GET] http://127.0.0.1:3000/myapp
      INFO [ Heim::Deploy ] Written and deployed application `myapp`.


      From here, you can see that we are able to access the application on http://127.0.0.1:3000/myapp:

      curl
      heim@system:~/repos$ curl http://127.0.0.1:3000/myapp
      Hello World!


      To deploy the application to the cloud instance, we will run heim deploy with the --cloud flag. This will generate a release build as default, but it can be changed to a debug with the the --dev flag.

      This will deploy the application to your subdomain at my-subdomain.cloud.heim.dev

      heim cli
      heim@system:~/repos$ heim deploy --cloud
      INFO [ Heim::Deploy ] Building Component: myapp
      INFO [ heim_registry::registry ] "myapp":"0.1.0":d34c64efacf7d7aff4136c6d177f2ef97ec3326a0bd61d77221cad122d097284
      INFO [ Heim::Deploy ] Heim application `myapp` is now built and written to registry.
      INFO [ Heim::Pipeline ] Starting pipeline 'Deploy application'
      INFO [ Heim::Pipeline ] Storing unoptimized component state
      INFO [ Heim::Pipeline ] Checking if defined regions in app definition is valid
      INFO [ Heim::Pipeline ] Continuing with localhost region heim-localhost
      INFO [ Heim::Pipeline ] Getting region specific application environment values
      INFO [ Heim::Pipeline ] Set environment values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Getting region specific application scaling values
      INFO [ Heim::Pipeline ] Set scaling values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Getting capabilities
      INFO [ Heim::Pipeline ] Set capabilities values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Getting triggers
      INFO [ Heim::Pipeline ] Set trigger values for region 'heim-localhost'
      INFO [ Heim::Pipeline ] Component environment values are valid for all regions
      INFO [ Heim::Pipeline ] Checking for region with changes to build
      INFO [ Heim::Pipeline ] Region "heim-localhost" has been changed and will be built
      INFO [ Heim::Pipeline ] Storing component and optimized component
      INFO [ Heim::Pipeline ] Stored component at ''
      INFO [ Heim::Pipeline ] Stored optimized component at ''
      INFO [ Heim::Pipeline ] Storing region states
      INFO [ Heim::Pipeline ] Stored state for region(s) heim-localhost successfully
      INFO [ Heim::Pipeline ] Creating and caching application(s)
      INFO [ Heim::Pipeline ] Creating and caching application(s) for region(s) heim-localhost
      INFO [ Heim::Pipeline ] Created application with hash c75e711a22ed212cb5fca17d70848974cd540dfd9229697f84d759e22481569f for region(s) heim-localhost
      INFO [ Heim::Pipeline ] Application has been deployed for region(s) heim-localhost
      INFO [ Heim::Pipeline ] Pipeline 'Deploy application' finished with status 'Success'
      INFO [ Heim::Deploy ] Heim deploy pipeline was successful
      INFO [ Heim::Deploy ] Application is accaccessible at:
      INFO [ Heim::Deploy ] Application is accaccessible at:
      INFO [ Heim::Deploy ] [GET] https://my-subdomain.cloud.heim.dev/myapp
      INFO [ Heim::Deploy ] Written and deployed application `myapp`.


      And, in the heim runtime you will see something like this, if you started the runtime with the --verbose flag:

      INFO WasmComponentHandler{
      app_id="localhost:00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000:myapp:bbcb756cbf72e572fd47f3c299f44e1d083522aaa2696c3b0b24653deb884d3c"
      uri=/myapp
      }:
      heim_runtime::services::handlers::wasm_component_handler: 48:
      app_id="localhost:00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000:myapp:bbcb756cbf72e572fd47f3c299f44e1d083522aaa2696c3b0b24653deb884d3c"
      uri=/myapp method=GET
      trigger=Trigger {
      app_id: AppId("localhost:00000000-0000-0000-0000-000000000000:00000000-0000-0000-0000-000000000000:myapp:bbcb756cbf72e572fd47f3c299f44e1d083522aaa2696c3b0b24653deb884d3c"),
      path: "/myapp",
      method: "GET",
      function_target: None,
      hash: "",
      domain: "127.0.0.1"
      }

      The Heim runtime output above summarizes all the basic information about your application.




  4. Now, we can make our specific Go application a little bit easier to work with.


    Let’s add an environment variable to our component.toml, while we’re at it:

    component.toml

    name = "myapp"
    version = "0.1.0"
    [build.dev]
    build = "tinygo build -target=wasip2 --wit-package ./wit/proxy.wit --wit-world nor2:heim/nor2-world -o bin/myapp.wasm main.go"
    wasm_path = "bin/myapp.wasm"
    [build.prod]
    build = "tinygo build -target=wasip2 --wit-package ./wit/proxy.wit --wit-world nor2:heim/nor2-world -gc=conservative -opt=z -o bin/myapp.wasm main.go"
    wasm_path = "bin/myapp.wasm"
    [input.env]
    HELLO_MESSAGE = { type = "String" }


    While we’re at it we can change the HTTP method to POST, and set the path to a new value. Furthermore, we’ll set a value to the HELLO_MESSAGE environment variable:

    application.toml

    name = "myapp"
    component_target = 'myapp:0.1.0'
    [trigger.http]
    type = 'http'
    path = '/mypath'
    path = '/myapp'
    method = 'POST'
    method = 'GET'
    [env]
    HELLO_MESSAGE = "Hi"


    Finally, we’ll update the code to use the new environment variable, and modify our code to expect a JSON body, and return the combined value of the environment variable with the name passed in as plaintext.

    main.go

    package main
    import (
    "bytes"
    "encoding/json"
    "fmt"
    incominghandler "go-http/wasi/http/incoming-handler"
    "go-http/wasi/http/types"
    "os"
    "go.bytecodealliance.org/cm"
    )
    func init() {
    incominghandler.Exports.Handle = Handle
    }
    func main() {}
    type Data struct {
    Name string `json:"name"`
    }
    func Handle(request types.IncomingRequest, responseOutParam types.ResponseOutparam) {
    incomingContentType := request.Headers().Get("Content-Type").Slice()
    contentTypeFound := false
    for _, val := range incomingContentType {
    if string(val.Slice()) == "application/json" {
    contentTypeFound = true
    break
    }
    }
    if !contentTypeFound {
    sendResponse(responseOutParam, 415, "text/plain", "Missing header Content-Type: application/json")
    return
    }
    incomingBody, _, isErr := request.Consume().Result()
    if isErr {
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Request body was read multiple times"))))
    return
    }
    incomingBodyStream, _, isErr := incomingBody.Stream().Result()
    if isErr {
    incomingBody.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Request body stream was read multiple times"))))
    return
    }
    var result bytes.Buffer
    const chunkSize = 1024
    for {
    readResult, _, _ := incomingBodyStream.BlockingRead(chunkSize).Result()
    if readResult.Len() == 0 {
    break
    }
    result.Write(readResult.Slice())
    }
    incomingBodyStream.ResourceDrop()
    incomingBody.ResourceDrop()
    var data Data
    if err := json.Unmarshal(result.Bytes(), &data); err != nil {
    sendResponse(responseOutParam, 400, "text/plain", "Incoming request body was malformed or missing")
    return
    }
    env, exists := os.LookupEnv("HELLO_MESSAGE")
    if !exists {
    sendResponse(responseOutParam, 400, "text/plain", "No environment variable with the name 'HELLO_MESSAGE' was found")
    return
    }
    sendResponse(responseOutParam, 200, "text/plain", fmt.Sprintf("%s %s", env, data.Name))
    }
    func sendResponse(responseOutParam types.ResponseOutparam, statuscode int, contentType string, message string) {
    // Set headers
    headers := types.NewFields()
    header := []byte(contentType)
    _, _, isErr := headers.Append("Content-Type", types.FieldValue(cm.NewList(&header[0], len(header)))).Result()
    if isErr {
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Failed to append headers"))))
    return
    }
    // Create response
    response := types.NewOutgoingResponse(headers)
    response.SetStatusCode(types.StatusCode(statuscode))
    // Write and finalize body
    body, _, isErr := response.Body().Result()
    if isErr {
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Body was read multiple times"))))
    return
    }
    bodyStream, _, isErr := body.Write().Result()
    if isErr {
    body.ResourceDrop()
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Body stream was read multiple times"))))
    return
    }
    bodyContent := []byte(message)
    _, _, isErr = bodyStream.Write(cm.NewList(&bodyContent[0], len(bodyContent))).Result()
    if isErr {
    body.ResourceDrop()
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Attempted to write to a closed stream"))))
    return
    }
    bodyStream.ResourceDrop()
    _, _, isErr = types.OutgoingBodyFinish(body, cm.None[types.Trailers]()).Result()
    if isErr {
    body.ResourceDrop()
    response.ResourceDrop()
    headers.ResourceDrop()
    types.ResponseOutparamSet(responseOutParam, cm.Err[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](types.ErrorCodeInternalError(cm.Some("Attempted to write to a closed stream"))))
    return
    }
    // Send the response
    types.ResponseOutparamSet(responseOutParam, cm.OK[cm.Result[types.ErrorCodeShape, types.OutgoingResponse, types.ErrorCode]](response))
    }


    Since the runtime is already running, we just need to build and deploy the application again, for our changes to take effect.

    heim cli
    heim@system:~/repos$ heim deploy
    INFO [ Heim::Deploy ] Building Component: myapp
    INFO [ heim_registry::registry ] "myapp":"0.1.0":f0c3d862345e6e52cd2cbf7eabbdd1ad21615f75f4d07bb0a702f8a6d4a46a57
    INFO [ Heim::Deploy ] Heim application `myapp` is now built and written to registry.
    INFO [ Heim::Pipeline ] Starting pipeline 'Deploy application'
    INFO [ Heim::Pipeline ] Storing unoptimized component state
    INFO [ Heim::Pipeline ] Checking if defined regions in app definition is valid
    INFO [ Heim::Pipeline ] Continuing with localhost region heim-localhost
    INFO [ Heim::Pipeline ] Getting region specific application environment values
    INFO [ Heim::Pipeline ] Set environment values for region 'heim-localhost'
    INFO [ Heim::Pipeline ] Getting region specific application scaling values
    INFO [ Heim::Pipeline ] Set scaling values for region 'heim-localhost'
    INFO [ Heim::Pipeline ] Getting capabilities
    INFO [ Heim::Pipeline ] Set capabilities values for region 'heim-localhost'
    INFO [ Heim::Pipeline ] Getting triggers
    INFO [ Heim::Pipeline ] Set trigger values for region 'heim-localhost'
    INFO [ Heim::Pipeline ] Component environment values are valid for all regions
    INFO [ Heim::Pipeline ] Checking for region with changes to build
    INFO [ Heim::Pipeline ] Region "heim-localhost" has been changed and will be built
    INFO [ Heim::Pipeline ] Storing component and optimized component
    INFO [ Heim::Pipeline ] Stored component at ''
    INFO [ Heim::Pipeline ] Stored optimized component at ''
    INFO [ Heim::Pipeline ] Storing region states
    INFO [ Heim::Pipeline ] Stored state for region(s) heim-localhost successfully
    INFO [ Heim::Pipeline ] Creating and caching application(s)
    INFO [ Heim::Pipeline ] Creating and caching application(s) for region(s) heim-localhost
    INFO [ Heim::Pipeline ] Created application with hash 6b162d04546821fe44b555680b3fdecede4f61732efbb71e6791362ee294b317 for region(s) heim-localhost
    INFO [ Heim::Pipeline ] Application has been deployed for region(s) heim-localhost
    INFO [ Heim::Pipeline ] Pipeline 'Deploy application' finished with status 'Success'
    INFO [ Heim::Deploy ] Heim deploy pipeline was successful
    INFO [ Heim::Deploy ] Application is accaccessible at:
    INFO [ Heim::Deploy ] [POST] http://127.0.0.1:3000/mypath
    INFO [ Heim::Deploy ] Written and deployed application `myapp`.


    curl
    heim@system:~/repos$ curl.exe http://127.0.0.1:3000/mypath -X POST
    Missing header Content-Type: application/json
    heim@system:~/repos$ curl.exe -X POST "http://127.0.0.1:3000/mypath" -H "Content-Type: application/json"
    Incoming request body was malformed or missing
    heim@system:~/repos$ curl.exe -X POST "http://127.0.0.1:3000/mypath" -H "Content-Type: application/json" -d '{\"name\":\"Heim\"}'
    Hi Heim




  5. When we are done, we can use the shortcut Ctrl+c to shut down the runtime.


    To clean up any resources from the guide you can run heim clear. This will remove all data from the cache.











    • run heim new
    • select the go-http template
    • choose HTTP method
    • choose trigger path
    • set application name


    • open the created application folder/project in your text editor

    Take note of the configuration files:



    • open main.go:

    • run heim start to start the Heim runtime

    • run heim deploy to build and deploy the application.

    • run heim deploy --cloud to deploy the application to the cloud instance.



    • edit the component.toml file to add environment variables
    • edit the application.toml file to change the HTTP method
    • edit the application.toml file to change the trigger path
    • edit the application.toml file to set environment variable values
    • edit the main.go file to use new environment variables
    • edit the main.go file to set up return and accept values
    • deploy the application again to see your changes


    • use the Ctrl+c shortcut to shut down the Heim runtime
    • use the heim clear command to remove all data from cache