github.com/syumai/workers

標準の net/http ハンドラを Cloudflare Workers 上で動かす Go パッケージ(syumai 作)。Go を Workers で動かすの定番実装で、Go→WASM 化と JS グルーを隠蔽し、既存の Go HTTP コードがほぼそのままエッジで動くようにする。experimental と明記。

以下の記述は commit 779944d(2026-05-04, PR #203 merge)時点の clone に基づく。API は experimental で変わりうるため、版を固定して確認した事実を残す。

何をしているか(中核)

Workers の FetchEvent を Go の http.Handler に橋渡しするのが本質。

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "hello")
    })
    workers.Serve(nil)          // or workers.Serve(handler)
}

裏側の流れ(syumai/workers が隠す部分):

JS エントリが fetch を受ける
  → wasm を instantiate
  → JS の Request を Go の *http.Request に変換して export 関数を呼ぶ
  → Go が書いた http.ResponseWriter を JS の Response に戻す

→ chi / gorilla/mux / 標準 ServeMux など既存ルータがそのまま乗るのが嬉しさ。WASM が外界に繋がれない問題(wasm-core)を、この変換層で吸収している。

どうやって JS と話すか(syscall/js)

Go の wasm(GOOS=js)は syscall/js パッケージで JS の値を掴み・呼べる。これが唯一の JS↔Go の通路で、syumai/workers は Cloudflare ランタイムの JS オブジェクト(Request / Response / env のバインディング)を syscall/js 越しにラップし、Go の型に見せている。

[JS shim] worker.mjs + wasm_exec_go.js / wasm_exec_tinygo.js
  - workers-assets-gen が生成(runtime/cloudflare.mjs 等)
  - wasm を instantiate し、fetch/scheduled/queue を wasm の export に配線
        ↕ syscall/js (js.Value)
[Go (wasm)]
  - js.Value で Request を読み、Response を組み立てる
  - cloudflare/* サブパッケージが env バインディングを Go API でラップ

JS 資産(wasm_exec + worker.mjs)は付属ツール workers-assets-gen が吐く。go.mod は go 1.21.3。パッケージは core の workers(Serve / ServeNonBlock)と cloudflare/{kv,r2,d1,cache,cron,queues,sockets,fetch} サブパッケージに分かれる。

非同期 I/O の橋渡し

Cloudflare のバインディング(KV/R2/fetch…)は JS 側で Promise を返す。Go の wasm はそれを syscall/js のコールバック → Go チャネルで待つ定石で同期的なコードに見せる(ライブラリが隠蔽)。goroutine は isolate の単一スレッド上で協調スケジューリングされ、OS スレッド並列は無いが、Promise 待ち中に他 goroutine は進める。

HTTP 以外のハンドラ

機能用途
FetchEventHTTP リクエスト処理(中核)
Cron Triggersスケジュール実行(scheduled handler)
QueuesProducer / Consumer
TCP Sockets外部への TCP 接続

Cloudflare バインディングへの Go API

JS バインディングを Go から叩けるよう薄くラップしている。

バインディングGo API(cloudflare/*)
KVkv.NewNamespace("VAR") → GetString / GetReader / PutString / PutReader / List / Delete
R2Head / Get / Put / Delete / List
Cache API対応
Durable Objectsstub 呼び出し
D1database/sql ドライバとして実装(init でドライバ登録 → sql.Open("d1", …) で使える)
Croncron.ScheduleTask(task) / ScheduleTaskNonBlock
環境変数cloudflare.Getenv 等
// KV: 標準的な Go API として叩ける(内部は syscall/js)
ns, _ := kv.NewNamespace("MY_KV")
_ = ns.PutString("k", "v", nil)
v, _ := ns.GetString("k", nil)

D1 が database/sql ドライバなのが効いていて、既存の Go の SQL コード(sql.Open/Query/Exec)がそのまま乗る — net/http がそのまま乗るのと同じ「既存資産の再利用」思想。

ビルド/デプロイ

いつ使うか(代替との比較)

選択肢向くケース弱み
syumai/workers(Go+wasm)既存 Go 資産・型・ライブラリをエッジへ。net/http コードを再利用wasm サイズ→コールドスタート、JS↔wasm 境界、experimental
素の JS/TS Worker最小コールドスタート・最小サイズ・公式サポートGo 資産は使えない
Go の WASI 経路(GOOS=wasip1)WASI システムコール前提のコードWorkers の WASI は実験的、バインディング統合が薄い

→ 「Go で書きたい/既存 Go を載せたい」が動機なら syumai/workers、純粋に速さ・軽さ最優先なら素の JS。

注意点

関連

Graph