Skip to content
workers-go
Esc
navigateopen⌘Jpreview
On this page

Deno / Deno Deploy

Run a Go HTTP server on Deno and Deno Deploy with the worker-go template.

Deno is a modern JavaScript/TypeScript runtime, and Deno Deploy is its hosted edge platform. The worker-go template compiles your http.Handler to Wasm and serves it through Deno.serve — the same workers.Serve() API as on the other platforms.

Requirements

  • Deno 2.4.2 or later (required for deno deploy)
  • Go 1.24.0 or later

Create a project

Scaffold the template with npx degit (requires Node.js and npm just for this step):

npx degit github:syumai/workers-go/_templates/deno/worker-go my-app
cd my-app
go mod init
go mod tidy
deno task build

Start the dev server:

deno task dev
curl http://localhost:8000/hello # outputs "Hello!"

How it works

deno task build runs workers-assets-gen -runtime=deno -mode=go to emit the runtime assets into ./build, then go build compiles your Go code to ./build/app.wasm. Unlike the Cloudflare and browser runtimes, Deno gets its own entry point: main.mjs, which simply calls Deno.serve on the worker’s fetch handler. runtime.mjs loads the Wasm binary via Deno.readFile for file: URLs and WebAssembly.compileStreaming when served over HTTP.

The runtime context exposes Deno environment variables through the same env shape used by the Cloudflare runtime, so cloudflare.Getenv keeps working — the proxy defers Deno.env.get calls, so no --allow-env permission is needed unless a variable is actually read.

On the Go side it is an ordinary http.Handler:

func main() {
	http.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte("Hello!"))
	})
	workers.Serve(nil) // if nil is given, http.DefaultServeMux is used
}

Deno runtime APIs

The experimental exp/deno package provides Go bindings for Deno runtime APIs (Deno.openKv, Deno.env, Deno.cron, system information, and more), generated by scripts/gen-deno from deno doc --json output (make gen-deno).

kv, err := deno.OpenKv(":memory:")
if err != nil {
	panic(err)
}
defer kv.Close()

Locally, Deno.openKv requires the --unstable-kv flag (add it to the dev task in deno.json). On Deno Deploy, unstable APIs are gated by the unstable field in deno.json; the template already sets "unstable": ["kv", "cron"] and adds ./deno.json to deploy.include so the deployed runtime reads it.

Cron jobs

Deno Deploy discovers cron jobs by evaluating a module’s top-level JavaScript at deployment time — a Deno.cron() call made from Go (via deno.Cron) runs only once a request boots the Wasm module, so it is never seen by the scheduler. Declare each job’s schedule in crons.mjs at the project root instead; workers-assets-gen copies it into build/ next to main.mjs, which imports it:

// crons.mjs (project root)
import worker from "./worker.mjs";
Deno.cron("my-job", "0 * * * *", () => worker.cron("my-job"));

and register the handler in Go with deno.OnCron:

deno.OnCron("my-job", func() {
	// runs every hour
})

Locally, Deno.cron requires the --unstable-cron flag (add it to the dev task in deno.json). deno.Cron still works under deno run for jobs you only need locally, but only the crons.mjs + deno.OnCron pair works on Deno Deploy.

Commands

Command Description
deno task dev Run the dev server (deno run --allow-net --allow-read --allow-env build/main.mjs)
deno task build Generate runtime assets and build the Go Wasm binary
deno task deploy Deploy to Deno Deploy (deno deploy)

To deploy, replace "org": "<TBD>" in deno.json with your Deno Deploy organization slug (or leave it and let deno deploy prompt you), then run deno task build && deno task deploy. On the first run, deno deploy interactively lets you select or create an app and saves deploy.app back into deno.json. Use deno deploy --prod to deploy to production.

Was this page helpful?