Basic Go Application
Prerequisites
Section titled “Prerequisites”- Language-specific tools - Install Heim Prerequisites
- Heim cli and runtime - Install Heim
- Text Editor - VsCode is a great option with language-specific plugins
- Terminal - Heim is accessed through its command-line interface
Start by running
heim newto 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-httpcsharp-httpcsharp-http-async...❯ go-http...python-httprust-httptypescript-httpopenapi-specificationNext, 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) › /myappLast, 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) › myappRendered 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 SuccessOnce we’ve gone through all the previous steps, we’ll have a project ready to go.
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.
component.toml
Section titled “component.toml”The component.toml file, defines a component, and one or more components make up an application in Heim.
component.tomlname = "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
application.toml
Section titled “application.toml”The application.toml is what defines an application. A application.toml is required for deploying an application to Heim.
application.tomlname = "myapp"component_target = 'myapp:0.1.0'[[trigger]]type = 'http'path = '/myapp'method = 'GET'a simple application.toml, with explanatory comments
Open
main.goand take a look at the code we have:main.gopackage mainimport (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 headersheaders := 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 responseresponse := types.NewOutgoingResponse(headers)response.SetStatusCode(types.StatusCode(http.StatusOK))// Write and finalize bodybody, _, 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 responsetypes.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.
Run heim start, to start the heim runtime:
heim runtime heim@system:~/repos$ heim startStarting Runtime with Config:...Http server listening on: http://127.0.0.1:3000Heim 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.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
--releaseflag:heim cli heim@system:~/repos$ heim deployINFO [ Heim::Deploy ] Building Component: myappINFO [ heim_registry::registry ] "myapp":"0.1.0":d34c64efacf7d7aff4136c6d177f2ef97ec3326a0bd61d77221cad122d097284INFO [ 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 stateINFO [ Heim::Pipeline ] Checking if defined regions in app definition is validINFO [ Heim::Pipeline ] Continuing with localhost region heim-localhostINFO [ Heim::Pipeline ] Getting region specific application environment valuesINFO [ Heim::Pipeline ] Set environment values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting region specific application scaling valuesINFO [ Heim::Pipeline ] Set scaling values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting capabilitiesINFO [ Heim::Pipeline ] Set capabilities values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting triggersINFO [ Heim::Pipeline ] Set trigger values for region 'heim-localhost'INFO [ Heim::Pipeline ] Component environment values are valid for all regionsINFO [ Heim::Pipeline ] Checking for region with changes to buildINFO [ Heim::Pipeline ] Region "heim-localhost" has been changed and will be builtINFO [ Heim::Pipeline ] Storing component and optimized componentINFO [ Heim::Pipeline ] Stored component at ''INFO [ Heim::Pipeline ] Stored optimized component at ''INFO [ Heim::Pipeline ] Storing region statesINFO [ Heim::Pipeline ] Stored state for region(s) heim-localhost successfullyINFO [ Heim::Pipeline ] Creating and caching application(s)INFO [ Heim::Pipeline ] Creating and caching application(s) for region(s) heim-localhostINFO [ Heim::Pipeline ] Created application with hash c75e711a22ed212cb5fca17d70848974cd540dfd9229697f84d759e22481569f for region(s) heim-localhostINFO [ Heim::Pipeline ] Application has been deployed for region(s) heim-localhostINFO [ Heim::Pipeline ] Pipeline 'Deploy application' finished with status 'Success'INFO [ Heim::Deploy ] Heim deploy pipeline was successfulINFO [ Heim::Deploy ] Application is accaccessible at:INFO [ Heim::Deploy ] [GET] http://127.0.0.1:3000/myappINFO [ 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/myappHello World!To deploy the application to the cloud instance, we will run heim deploy with the
--cloudflag. This will generate a release build as default, but it can be changed to a debug with the the--devflag.This will deploy the application to your subdomain at
my-subdomain.cloud.heim.devheim cli heim@system:~/repos$ heim deploy --cloudINFO [ Heim::Deploy ] Building Component: myappINFO [ heim_registry::registry ] "myapp":"0.1.0":d34c64efacf7d7aff4136c6d177f2ef97ec3326a0bd61d77221cad122d097284INFO [ 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 stateINFO [ Heim::Pipeline ] Checking if defined regions in app definition is validINFO [ Heim::Pipeline ] Continuing with localhost region heim-localhostINFO [ Heim::Pipeline ] Getting region specific application environment valuesINFO [ Heim::Pipeline ] Set environment values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting region specific application scaling valuesINFO [ Heim::Pipeline ] Set scaling values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting capabilitiesINFO [ Heim::Pipeline ] Set capabilities values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting triggersINFO [ Heim::Pipeline ] Set trigger values for region 'heim-localhost'INFO [ Heim::Pipeline ] Component environment values are valid for all regionsINFO [ Heim::Pipeline ] Checking for region with changes to buildINFO [ Heim::Pipeline ] Region "heim-localhost" has been changed and will be builtINFO [ Heim::Pipeline ] Storing component and optimized componentINFO [ Heim::Pipeline ] Stored component at ''INFO [ Heim::Pipeline ] Stored optimized component at ''INFO [ Heim::Pipeline ] Storing region statesINFO [ Heim::Pipeline ] Stored state for region(s) heim-localhost successfullyINFO [ Heim::Pipeline ] Creating and caching application(s)INFO [ Heim::Pipeline ] Creating and caching application(s) for region(s) heim-localhostINFO [ Heim::Pipeline ] Created application with hash c75e711a22ed212cb5fca17d70848974cd540dfd9229697f84d759e22481569f for region(s) heim-localhostINFO [ Heim::Pipeline ] Application has been deployed for region(s) heim-localhostINFO [ Heim::Pipeline ] Pipeline 'Deploy application' finished with status 'Success'INFO [ Heim::Deploy ] Heim deploy pipeline was successfulINFO [ Heim::Deploy ] Application is accaccessible at:INFO [ Heim::Deploy ] Application is accaccessible at:INFO [ Heim::Deploy ] [GET] https://my-subdomain.cloud.heim.dev/myappINFO [ 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
--verboseflag: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=GETtrigger=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.
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.tomlname = "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 methodtoPOST, and set thepathto a new value. Furthermore, we’ll set a value to theHELLO_MESSAGEenvironment variable:application.tomlname = "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.gopackage mainimport ("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 := falsefor _, val := range incomingContentType {if string(val.Slice()) == "application/json" {contentTypeFound = truebreak}}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.Bufferconst chunkSize = 1024for {readResult, _, _ := incomingBodyStream.BlockingRead(chunkSize).Result()if readResult.Len() == 0 {break}result.Write(readResult.Slice())}incomingBodyStream.ResourceDrop()incomingBody.ResourceDrop()var data Dataif 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 headersheaders := 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 responseresponse := types.NewOutgoingResponse(headers)response.SetStatusCode(types.StatusCode(statuscode))// Write and finalize bodybody, _, 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 responsetypes.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 deployINFO [ Heim::Deploy ] Building Component: myappINFO [ heim_registry::registry ] "myapp":"0.1.0":f0c3d862345e6e52cd2cbf7eabbdd1ad21615f75f4d07bb0a702f8a6d4a46a57INFO [ 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 stateINFO [ Heim::Pipeline ] Checking if defined regions in app definition is validINFO [ Heim::Pipeline ] Continuing with localhost region heim-localhostINFO [ Heim::Pipeline ] Getting region specific application environment valuesINFO [ Heim::Pipeline ] Set environment values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting region specific application scaling valuesINFO [ Heim::Pipeline ] Set scaling values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting capabilitiesINFO [ Heim::Pipeline ] Set capabilities values for region 'heim-localhost'INFO [ Heim::Pipeline ] Getting triggersINFO [ Heim::Pipeline ] Set trigger values for region 'heim-localhost'INFO [ Heim::Pipeline ] Component environment values are valid for all regionsINFO [ Heim::Pipeline ] Checking for region with changes to buildINFO [ Heim::Pipeline ] Region "heim-localhost" has been changed and will be builtINFO [ Heim::Pipeline ] Storing component and optimized componentINFO [ Heim::Pipeline ] Stored component at ''INFO [ Heim::Pipeline ] Stored optimized component at ''INFO [ Heim::Pipeline ] Storing region statesINFO [ Heim::Pipeline ] Stored state for region(s) heim-localhost successfullyINFO [ Heim::Pipeline ] Creating and caching application(s)INFO [ Heim::Pipeline ] Creating and caching application(s) for region(s) heim-localhostINFO [ Heim::Pipeline ] Created application with hash 6b162d04546821fe44b555680b3fdecede4f61732efbb71e6791362ee294b317 for region(s) heim-localhostINFO [ Heim::Pipeline ] Application has been deployed for region(s) heim-localhostINFO [ Heim::Pipeline ] Pipeline 'Deploy application' finished with status 'Success'INFO [ Heim::Deploy ] Heim deploy pipeline was successfulINFO [ Heim::Deploy ] Application is accaccessible at:INFO [ Heim::Deploy ] [POST] http://127.0.0.1:3000/mypathINFO [ Heim::Deploy ] Written and deployed application `myapp`.curl heim@system:~/repos$ curl.exe http://127.0.0.1:3000/mypath -X POSTMissing header Content-Type: application/jsonheim@system:~/repos$ curl.exe -X POST "http://127.0.0.1:3000/mypath" -H "Content-Type: application/json"Incoming request body was malformed or missingheim@system:~/repos$ curl.exe -X POST "http://127.0.0.1:3000/mypath" -H "Content-Type: application/json" -d '{\"name\":\"Heim\"}'Hi HeimWhen we are done, we can use the shortcut
Ctrl+cto 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.
Summary
Section titled “Summary”-
- run
heim new - select the go-http
template - choose
HTTP method - choose trigger
path - set application
name
- run
-
- open the created application folder/project in your text editor
Take note of the configuration files:
-
- open
main.go:
- run
heim startto start the Heim runtime
- run
heim deployto build and deploy the application.
- run
heim deploy --cloudto deploy the application to the cloud instance.
- open
-
- edit the
component.tomlfile to add environment variables - edit the
application.tomlfile to change the HTTP method - edit the
application.tomlfile to change the trigger path - edit the
application.tomlfile to set environment variable values - edit the
main.gofile to use new environment variables - edit the
main.gofile to set up return and accept values - deploy the application again to see your changes
- edit the
-
- use the
Ctrl+cshortcut to shut down the Heim runtime - use the
heim clearcommand to remove all data from cache
- use the