Synopsis
I wanted to run an application in Go that returned plain HTML,
I decided to use Cloudflare Workers, which can convert it to Wasm and deploy it.
I recommend syumai/workers's template for deploying Go applications to Cloudflare Workers.
syumai/workers: Go package to run an HTTP server on Cloudflare Workers.
Go + text/template
First, let's build using text/template
in a straightforward manner.
❯ ls -lh . /build
total 15656
-rwxr-xr-x 1 ergofriend staff 7.6M 8 8 20:12 app.wasm
-rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:12 shim.mjs
-rw-r--r-- 1 ergofriend staff 16K 8 8 20:12 wasm_exec.js
-rw-r--r-- 1 ergofriend staff 160B 8 8 20:12 worker.mjs
It was almost 8 MB.
https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits
Cloudflare Workers has worker size limits depending on the plan.
The free slot is 1MB, and the paid slot ($5~) is 10MB.
Even with the final limit based on size after compression, it will be tough to fit into the free quota starting at 8MB.
TinyGo + text/template
So I decided to switch to TinyGo, which is supposed to be for WebAssembly (Wasm).
After building, the size is about 0.75 MB, which seems to fit into the free frame.
❯ ls -lh . /build
total 1160
-rwxr-xr-x 1 ergofriend staff 556K 8 8 20:23 app.wasm
-rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:23 shim.mjs
-rw-r--r-- 1 ergofriend staff 15K 8 8 20:23 wasm_exec.js
-rw-r--r-- 1 ergofriend staff 160B 8 8 20:23 worker.mjs
Oops!
But here is where tragedy strikes.
When I try to access the built application, I get an error.
[wrangler:inf] GET / 200 OK (35ms)
✘ [ERROR] Uncaught (in response) RuntimeError: unreachable
at main.runtime._panic (wasm://wasm/main-0022bc46:wasm-function[35]:0x2b4a)
at main.(*text/template.state).evalField
(wasm://wasm/main-0022bc46:wasm-function[540]:0x6c5f4)
at main.(*text/template.state).evalFieldChain
(wasm://wasm/main-0022bc46:wasm-function[531]:0x697fe)
at main.(*text/template.state).evalFieldNode
(wasm://wasm/main-0022bc46:wasm-function[530]:0x6959a)
at main.(*text/template.state).evalPipeline
(wasm://wasm/main-0022bc46:wasm-function[535]:0x6a1d2)
at main.(*text/template.state).walk (wasm://wasm/main-0022bc46:wasm-function[569]:0x72cdd)
at main.(*text/template.state).walk (wasm://wasm/main-0022bc46:wasm-function[569]:0x730b0)
at main.main$1 (wasm://wasm/main-0022bc46:wasm-function[261]:0x2ac52)
at main.(net/http.HandlerFunc).ServeHTTP
(wasm://wasm/main-0022bc46:wasm-function[463]:0x5973a)
at
main.interface:{ServeHTTP:func:{named:net/http.ResponseWriter,pointer:named:net/http.Request}{}}.ServeHTTP$invoke
(wasm://wasm/main-0022bc46:wasm-function[459]:0x56f72)
panic: unimplemented: (reflect.Value).MethodByName()
The method MethodByName
, which is called when a template variable is passed in template.ExecuteTemplate(), is not yet It seems to be unimplemented.
Since TinyGo is a subset of the original, there are many unsupported features,
I found that text/template
was calling unsupported methods.
Now, I would like to be cool and say p-r for TinyGo, but this time I'm going to look for a quick alternative.
TEMPL
So I was looking for another template engine to replace text/template
and came across templ.
An HTML templating language for Go that has great developer tooling.
Documentation
See user documentation at https://templ.guide
Tasks
build
Build a local version.
go run ./get-version > .version
cd cmd/templ
go build
nix-update-gomod2nix
gomod2nix
install-snapshot
Build and install current version.
# Remove templ from the non-standard ~/bin/templ path
# that this command previously used.
rm -f ~/bin/templ
# Clear LSP logs.
rm -f cmd/templ/lspcmd/*.txt
# Update version.
go run ./get-version > .version
# Install to $GOPATH/bin or $HOME/go/bin
cd cmd/templ && go install
build-snapshot
Use goreleaser to build the command line binary using goreleaser.
goreleaser build --snapshot --clean
generate
Run templ generate using local version.
go run ./cmd/templ generate -include-version=false
test
Run Go tests.
go run ./get-version > .version
go run ./cmd/templ generate -include-version=false
go test ./...
test-short
Run Go tests.
go run ./get-version > .version
go run ./cmd/templ generate -include-version=false
go test ./... -short
test-cover
Run…
Features
Here is a summary of TEMPL.
Own DSL
It is not difficult to write a unique program, but it can be written like templ ≈ Go + JSX
.
templ hello() {
<div>Welcome back!
}
It looks like a very familiar writing style.
templ login(isLoggedIn bool) {
if isLoggedIn {
@hello() <! -- @ to use other components -->.
} else {
<input name="login" type="button" value="Log in"/>
}
}
When you use it, you call Go functions generated from the DSL by templ generate
on a component-by-component basis.
func login(isLoggedIn bool) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5
...
}
Handle("/", templ.Handler(login(false))))
The function of the generated component inherits the argument types defined in the template, so it can be safely called.
Unlike some ExecuteTemplate, this is safe.
VSCode Extensions
https://marketplace.visualstudio.com/items?itemName=a-h.templ
Syntax Highlight and LSP completion will be very useful.
TinyGo + templ
Just to be sure, I checked and found that it only depends on reflect's TypeOf
, which is already implemented in TinyGo.
Now let's try using templ.
The build seemed to have enough room.
❯ ls -lh . /build
total 1008
-rwxr-xr-x 1 ergofriend staff 477K 8 8 20:35 app.wasm
-rw-r--r-- 1 ergofriend staff 1.2K 8 8 20:35 shim.mjs
-rw-r--r-- 1 ergofriend staff 15K 8 8 20:35 wasm_exec.js
-rw-r--r-- 1 ergofriend staff 160B 8 8 20:35 worker.mjs
Here is the actual application.
goworkers-demo.ergofriend.workers.dev
When accessed, the HTML can be returned without error.
> wrangler deploy
Total Upload: 493.48 KiB / gzip: 187.91 KiB
Total Upload: 493.48 KiB / gzip: 187.91 KiB
The final deployed size is also 187.91 KiB
, so there is plenty of room to expand the application.
This verification is left in this repository.
ergofriend/goworkers-demo
This article is a translation from Japanese.
https://ergofriend.hatenablog.com/entry/2024/08/08/230603
Top comments (0)