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 以外のハンドラ
| 機能 | 用途 |
|---|---|
| FetchEvent | HTTP リクエスト処理(中核) |
| Cron Triggers | スケジュール実行(scheduled handler) |
| Queues | Producer / Consumer |
| TCP Sockets | 外部への TCP 接続 |
Cloudflare バインディングへの Go API
JS バインディングを Go から叩けるよう薄くラップしている。
| バインディング | Go API(cloudflare/*) |
|---|---|
| KV | kv.NewNamespace("VAR") → GetString / GetReader / PutString / PutReader / List / Delete |
| R2 | Head / Get / Put / Delete / List |
| Cache API | 対応 |
| Durable Objects | stub 呼び出し |
| D1 | database/sql ドライバとして実装(init でドライバ登録 → sql.Open("d1", …) で使える) |
| Cron | cron.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 がそのまま乗るのと同じ「既存資産の再利用」思想。
ビルド/デプロイ
npm create cloudflare@latestのテンプレート(_templates/cloudflare/worker-go/worker-tinygo)から初期化go mod init→npm start(開発)→ ビルドで Go→WASM →wranglerでデプロイ- 標準 Go か TinyGo を選択 — 依存が多い標準 Go は WASM サイズ上限(無料 3MB / 有料 10MB)に当たりやすく、その場合 TinyGo 推奨(go-on-cloudflare-workers 参照)
いつ使うか(代替との比較)
| 選択肢 | 向くケース | 弱み |
|---|---|---|
| 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。
注意点
- experimental — API は変わりうる
- コールドスタート — isolate ごとに wasm を instantiate するため、サイズが初回起動に直結。TinyGo +
wasm-opt(DCE)で削る - サイズ制約 → 大依存は TinyGo を検討
- isolate の制約(単一スレッド・サイズ=コールドスタート)は当然そのまま受ける
Links
- syumai/workers (GitHub) — 確認版 commit
779944db4752699bf9ff7386cd34d24ce3d5870d(2026-05-04)
関連
- go-on-cloudflare-workers — 「Go を Workers で動かす」一般論。syumai/workers はその具体実装
- wasm-core — WASM は I/O を持たない。だから fetch→Go の変換層(グルー)が要る
- v8-isolates — Workers の実行基盤。本ライブラリもこの制約下で動く
- edge-platforms — Cloudflare Workers を含むエッジ実行環境
- github-com-tinygo-org-tinygo — wasm を吐くコンパイラ。サイズ制約のため標準 Go でなくこれを使う